From c4f26ae3369740ca6a12197dc194135c5bb0ef54 Mon Sep 17 00:00:00 2001 From: Jonathan Blandford Date: Sat, 28 Apr 2001 00:29:34 +0000 Subject: [PATCH] Massive reorder/cleanup of a lot of the code. Some documentation added. Fri Apr 27 20:27:21 2001 Jonathan Blandford * gtk/gtktreeview.[hc]: Massive reorder/cleanup of a lot of the code. Some documentation added. --- ChangeLog | 5 + ChangeLog.pre-2-0 | 5 + ChangeLog.pre-2-10 | 5 + ChangeLog.pre-2-2 | 5 + ChangeLog.pre-2-4 | 5 + ChangeLog.pre-2-6 | 5 + ChangeLog.pre-2-8 | 5 + gtk/gtktreeview.c | 9162 ++++++++++++++++++++++---------------------- gtk/gtktreeview.h | 299 +- 9 files changed, 4821 insertions(+), 4675 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2b6bf3bcff..51da0db76f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Fri Apr 27 20:27:21 2001 Jonathan Blandford + + * gtk/gtktreeview.[hc]: Massive reorder/cleanup of a lot of the + code. Some documentation added. + 2001-04-27 Havoc Pennington * gtk/gtkcombo.c (gtk_combo_popup_button_press): fix warning diff --git a/ChangeLog.pre-2-0 b/ChangeLog.pre-2-0 index 2b6bf3bcff..51da0db76f 100644 --- a/ChangeLog.pre-2-0 +++ b/ChangeLog.pre-2-0 @@ -1,3 +1,8 @@ +Fri Apr 27 20:27:21 2001 Jonathan Blandford + + * gtk/gtktreeview.[hc]: Massive reorder/cleanup of a lot of the + code. Some documentation added. + 2001-04-27 Havoc Pennington * gtk/gtkcombo.c (gtk_combo_popup_button_press): fix warning diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index 2b6bf3bcff..51da0db76f 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,8 @@ +Fri Apr 27 20:27:21 2001 Jonathan Blandford + + * gtk/gtktreeview.[hc]: Massive reorder/cleanup of a lot of the + code. Some documentation added. + 2001-04-27 Havoc Pennington * gtk/gtkcombo.c (gtk_combo_popup_button_press): fix warning diff --git a/ChangeLog.pre-2-2 b/ChangeLog.pre-2-2 index 2b6bf3bcff..51da0db76f 100644 --- a/ChangeLog.pre-2-2 +++ b/ChangeLog.pre-2-2 @@ -1,3 +1,8 @@ +Fri Apr 27 20:27:21 2001 Jonathan Blandford + + * gtk/gtktreeview.[hc]: Massive reorder/cleanup of a lot of the + code. Some documentation added. + 2001-04-27 Havoc Pennington * gtk/gtkcombo.c (gtk_combo_popup_button_press): fix warning diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index 2b6bf3bcff..51da0db76f 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -1,3 +1,8 @@ +Fri Apr 27 20:27:21 2001 Jonathan Blandford + + * gtk/gtktreeview.[hc]: Massive reorder/cleanup of a lot of the + code. Some documentation added. + 2001-04-27 Havoc Pennington * gtk/gtkcombo.c (gtk_combo_popup_button_press): fix warning diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index 2b6bf3bcff..51da0db76f 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,8 @@ +Fri Apr 27 20:27:21 2001 Jonathan Blandford + + * gtk/gtktreeview.[hc]: Massive reorder/cleanup of a lot of the + code. Some documentation added. + 2001-04-27 Havoc Pennington * gtk/gtkcombo.c (gtk_combo_popup_button_press): fix warning diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index 2b6bf3bcff..51da0db76f 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,8 @@ +Fri Apr 27 20:27:21 2001 Jonathan Blandford + + * gtk/gtktreeview.[hc]: Massive reorder/cleanup of a lot of the + code. Some documentation added. + 2001-04-27 Havoc Pennington * gtk/gtkcombo.c (gtk_combo_popup_button_press): fix warning diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c index 93100f2310..1014544981 100644 --- a/gtk/gtktreeview.c +++ b/gtk/gtktreeview.c @@ -45,6 +45,7 @@ #endif +#define SCROLL_EDGE_SIZE 15 /* The "background" areas of all rows/cells add up to cover the entire tree. @@ -52,7 +53,6 @@ * The "cell" areas are the cell_area passed in to gtk_cell_renderer_render(), * i.e. just the cells, no spacing. */ - #define BACKGROUND_FIRST_PIXEL(tree_view,tree,node) (_gtk_rbtree_node_find_offset ((tree), (node)) + TREE_VIEW_HEADER_HEIGHT ((tree_view))) #define CELL_FIRST_PIXEL(tree_view,tree,node,separator) (BACKGROUND_FIRST_PIXEL (tree_view,tree,node) + separator/2) @@ -62,8 +62,8 @@ #define TREE_WINDOW_Y_TO_RBTREE_Y(tree_view,y) ((y) - TREE_VIEW_HEADER_HEIGHT (tree_view)) #define RBTREE_Y_TO_TREE_WINDOW_Y(tree_view,y) ((y) + TREE_VIEW_HEADER_HEIGHT (tree_view)) -typedef struct _GtkTreeViewChild GtkTreeViewChild; +typedef struct _GtkTreeViewChild GtkTreeViewChild; struct _GtkTreeViewChild { GtkWidget *widget; @@ -72,6 +72,23 @@ struct _GtkTreeViewChild }; +typedef struct _TreeViewDragInfo TreeViewDragInfo; +struct _TreeViewDragInfo +{ + GdkModifierType start_button_mask; + GtkTargetList *source_target_list; + GdkDragAction source_actions; + GClosure *row_draggable_closure; + + GtkTargetList *dest_target_list; + GClosure *location_droppable_closure; + + guint source_set : 1; + guint dest_set : 1; +}; + + +/* Signals */ enum { ROW_ACTIVATED, @@ -80,9 +97,9 @@ enum LAST_SIGNAL }; +/* Properties */ enum { PROP_0, - PROP_MODEL, PROP_HADJUSTMENT, PROP_VADJUSTMENT, @@ -92,8 +109,11 @@ enum { PROP_RULES_HINT }; -static void gtk_tree_view_init (GtkTreeView *tree_view); static void gtk_tree_view_class_init (GtkTreeViewClass *klass); +static void gtk_tree_view_init (GtkTreeView *tree_view); + +/* object signals */ +static void gtk_tree_view_finalize (GObject *object); static void gtk_tree_view_set_property (GObject *object, guint prop_id, const GValue *value, @@ -102,13 +122,11 @@ static void gtk_tree_view_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); -/* o signals */ -static void gtk_tree_view_finalize (GObject *object); -/* object signals */ +/* gtkobject signals */ static void gtk_tree_view_destroy (GtkObject *object); -/* widget signals */ +/* gtkwidget signals */ static void gtk_tree_view_setup_model (GtkTreeView *tree_view); static void gtk_tree_view_realize (GtkWidget *widget); static void gtk_tree_view_unrealize (GtkWidget *widget); @@ -265,7 +283,11 @@ static void _gtk_tree_view_update_col_width (GtkTreeView *tree_view) static GtkContainerClass *parent_class = NULL; static guint tree_view_signals[LAST_SIGNAL] = { 0 }; -/* Class Functions */ + + +/* GType Methods + */ + GtkType gtk_tree_view_get_type (void) { @@ -300,47 +322,49 @@ gtk_tree_view_class_init (GtkTreeViewClass *class) GtkWidgetClass *widget_class; GtkContainerClass *container_class; + parent_class = g_type_class_peek_parent (class); + o_class = (GObjectClass *) class; object_class = (GtkObjectClass *) class; widget_class = (GtkWidgetClass *) class; container_class = (GtkContainerClass *) class; - parent_class = g_type_class_peek_parent (class); - - o_class->finalize = gtk_tree_view_finalize; + /* GObject signals */ o_class->set_property = gtk_tree_view_set_property; o_class->get_property = gtk_tree_view_get_property; + o_class->finalize = gtk_tree_view_finalize; + /* GtkObject signals */ object_class->destroy = gtk_tree_view_destroy; + /* GtkWidget signals */ + widget_class->map = gtk_tree_view_map; widget_class->realize = gtk_tree_view_realize; widget_class->unrealize = gtk_tree_view_unrealize; - widget_class->map = gtk_tree_view_map; widget_class->size_request = gtk_tree_view_size_request; widget_class->size_allocate = gtk_tree_view_size_allocate; - widget_class->expose_event = gtk_tree_view_expose; + widget_class->button_press_event = gtk_tree_view_button_press; + widget_class->button_release_event = gtk_tree_view_button_release; widget_class->motion_notify_event = gtk_tree_view_motion; + widget_class->expose_event = gtk_tree_view_expose; widget_class->enter_notify_event = gtk_tree_view_enter_notify; widget_class->leave_notify_event = gtk_tree_view_leave_notify; - widget_class->button_press_event = gtk_tree_view_button_press; - widget_class->button_release_event = gtk_tree_view_button_release; widget_class->focus_in_event = gtk_tree_view_focus_in; widget_class->focus_out_event = gtk_tree_view_focus_out; - widget_class->drag_begin = gtk_tree_view_drag_begin; widget_class->drag_end = gtk_tree_view_drag_end; widget_class->drag_data_get = gtk_tree_view_drag_data_get; widget_class->drag_data_delete = gtk_tree_view_drag_data_delete; - widget_class->drag_leave = gtk_tree_view_drag_leave; widget_class->drag_motion = gtk_tree_view_drag_motion; widget_class->drag_drop = gtk_tree_view_drag_drop; widget_class->drag_data_received = gtk_tree_view_drag_data_received; - container_class->forall = gtk_tree_view_forall; + /* GtkContainer signals */ container_class->remove = gtk_tree_view_remove; - container_class->set_focus_child = gtk_tree_view_set_focus_child; + container_class->forall = gtk_tree_view_forall; container_class->focus = gtk_tree_view_focus; + container_class->set_focus_child = gtk_tree_view_set_focus_child; class->set_scroll_adjustments = gtk_tree_view_set_adjustments; @@ -382,7 +406,7 @@ gtk_tree_view_class_init (GtkTreeViewClass *class) _("Headers Clickable"), _("Column headers respond to click events"), FALSE, - G_PARAM_READWRITE)); + G_PARAM_WRITE)); g_object_class_install_property (o_class, PROP_EXPANDER_COLUMN, @@ -420,6 +444,7 @@ gtk_tree_view_class_init (GtkTreeViewClass *class) GTK_TYPE_NONE, 2, GTK_TYPE_TREE_PATH, GTK_TYPE_TREE_VIEW_COLUMN); + tree_view_signals[EXPAND_ROW] = g_signal_newc ("expand_row", G_TYPE_FROM_CLASS (object_class), @@ -430,6 +455,7 @@ gtk_tree_view_class_init (GtkTreeViewClass *class) G_TYPE_BOOLEAN, 2, GTK_TYPE_TREE_ITER, GTK_TYPE_TREE_PATH); + tree_view_signals[COLLAPSE_ROW] = g_signal_newc ("collapse_row", G_TYPE_FROM_CLASS (object_class), @@ -491,34 +517,100 @@ gtk_tree_view_init (GtkTreeView *tree_view) GTK_WIDGET_SET_FLAGS (tree_view, GTK_CAN_FOCUS); - tree_view->priv->model = NULL; tree_view->priv->flags = GTK_TREE_VIEW_IS_LIST | GTK_TREE_VIEW_SHOW_EXPANDERS | GTK_TREE_VIEW_DRAW_KEYFOCUS | GTK_TREE_VIEW_HEADERS_VISIBLE; gtk_widget_style_get (GTK_WIDGET (tree_view), "expander_width", &tree_view->priv->tab_offset, NULL); tree_view->priv->n_columns = 0; - tree_view->priv->columns = NULL; - tree_view->priv->button_pressed_node = NULL; - tree_view->priv->button_pressed_tree = NULL; - tree_view->priv->prelight_node = NULL; tree_view->priv->header_height = 1; tree_view->priv->x_drag = 0; tree_view->priv->drag_pos = -1; - tree_view->priv->selection = NULL; - tree_view->priv->anchor = NULL; - tree_view->priv->cursor = NULL; tree_view->priv->header_has_focus = FALSE; tree_view->priv->pressed_button = -1; tree_view->priv->press_start_x = -1; tree_view->priv->press_start_y = -1; - tree_view->priv->drag_window = NULL; gtk_tree_view_set_adjustments (tree_view, NULL, NULL); _gtk_tree_view_update_size (tree_view); } + -/* Object methods +/* GObject Methods */ +static void +gtk_tree_view_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkTreeView *tree_view; + + tree_view = GTK_TREE_VIEW (object); + + switch (prop_id) + { + case PROP_MODEL: + gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (g_value_get_object (value))); + break; + case PROP_HADJUSTMENT: + gtk_tree_view_set_hadjustment (tree_view, GTK_ADJUSTMENT (g_value_get_object (value))); + break; + case PROP_VADJUSTMENT: + gtk_tree_view_set_vadjustment (tree_view, GTK_ADJUSTMENT (g_value_get_object (value))); + break; + case PROP_HEADERS_VISIBLE: + gtk_tree_view_set_headers_visible (tree_view, g_value_get_boolean (value)); + break; + case PROP_HEADERS_CLICKABLE: + gtk_tree_view_set_headers_clickable (tree_view, g_value_get_boolean (value)); + break; + case PROP_EXPANDER_COLUMN: + gtk_tree_view_set_expander_column (tree_view, g_value_get_uint (value)); + break; + case PROP_RULES_HINT: + gtk_tree_view_set_rules_hint (tree_view, g_value_get_boolean (value)); + break; + default: + break; + } +} + +static void +gtk_tree_view_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkTreeView *tree_view; + + tree_view = GTK_TREE_VIEW (object); + + switch (prop_id) + { + case PROP_MODEL: + g_value_set_object (value, G_OBJECT (tree_view->priv->model)); + break; + case PROP_HADJUSTMENT: + g_value_set_object (value, G_OBJECT (tree_view->priv->hadjustment)); + break; + case PROP_VADJUSTMENT: + g_value_set_object (value, G_OBJECT (tree_view->priv->vadjustment)); + break; + case PROP_HEADERS_VISIBLE: + g_value_set_boolean (value, gtk_tree_view_get_headers_visible (tree_view)); + break; + case PROP_EXPANDER_COLUMN: + g_value_set_uint (value, tree_view->priv->expander_column); + break; + case PROP_RULES_HINT: + g_value_set_boolean (value, tree_view->priv->has_rules); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + static void gtk_tree_view_finalize (GObject *object) { @@ -530,6 +622,11 @@ gtk_tree_view_finalize (GObject *object) (* G_OBJECT_CLASS (parent_class)->finalize) (object); } + + +/* GtkObject Methods + */ + static void gtk_tree_view_destroy (GtkObject *object) { @@ -593,88 +690,77 @@ gtk_tree_view_destroy (GtkObject *object) (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); } -/* Property handlers + + +/* GtkWidget Methods */ +/* GtkWidget:;map helper */ static void -gtk_tree_view_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) +gtk_tree_view_map_buttons (GtkTreeView *tree_view) { - GtkTreeView *tree_view; + GList *list; - tree_view = GTK_TREE_VIEW (object); + g_return_if_fail (GTK_WIDGET_MAPPED (tree_view)); - switch (prop_id) + if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE)) { - case PROP_MODEL: - gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (g_value_get_object (value))); - break; - case PROP_HADJUSTMENT: - gtk_tree_view_set_hadjustment (tree_view, GTK_ADJUSTMENT (g_value_get_object (value))); - break; - case PROP_VADJUSTMENT: - gtk_tree_view_set_vadjustment (tree_view, GTK_ADJUSTMENT (g_value_get_object (value))); - break; - case PROP_HEADERS_VISIBLE: - gtk_tree_view_set_headers_visible (tree_view, g_value_get_boolean (value)); - break; - case PROP_HEADERS_CLICKABLE: - gtk_tree_view_set_headers_clickable (tree_view, g_value_get_boolean (value)); - break; - case PROP_EXPANDER_COLUMN: - gtk_tree_view_set_expander_column (tree_view, g_value_get_uint (value)); - break; - case PROP_RULES_HINT: - gtk_tree_view_set_rules_hint (tree_view, g_value_get_boolean (value)); - break; - default: - break; + GtkTreeViewColumn *column; + + for (list = tree_view->priv->columns; list; list = list->next) + { + column = list->data; + if (GTK_WIDGET_VISIBLE (column->button) && + !GTK_WIDGET_MAPPED (column->button)) + gtk_widget_map (column->button); + } + for (list = tree_view->priv->columns; list; list = list->next) + { + column = list->data; + if (column->visible == FALSE) + continue; + if (column->column_type == GTK_TREE_VIEW_COLUMN_RESIZEABLE) + { + gdk_window_raise (column->window); + gdk_window_show (column->window); + } + else + gdk_window_hide (column->window); + } + gdk_window_show (tree_view->priv->header_window); } } static void -gtk_tree_view_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) +gtk_tree_view_map (GtkWidget *widget) { + GList *tmp_list; GtkTreeView *tree_view; - tree_view = GTK_TREE_VIEW (object); + g_return_if_fail (GTK_IS_TREE_VIEW (widget)); - switch (prop_id) + tree_view = GTK_TREE_VIEW (widget); + + GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED); + + tmp_list = tree_view->priv->children; + while (tmp_list) { - case PROP_MODEL: - g_value_set_object (value, G_OBJECT (tree_view->priv->model)); - break; - case PROP_HADJUSTMENT: - g_value_set_object (value, G_OBJECT (tree_view->priv->hadjustment)); - break; - case PROP_VADJUSTMENT: - g_value_set_object (value, G_OBJECT (tree_view->priv->vadjustment)); - break; - case PROP_HEADERS_VISIBLE: - g_value_set_boolean (value, gtk_tree_view_get_headers_visible (tree_view)); - break; - case PROP_HEADERS_CLICKABLE: - /* g_value_set_boolean (value, gtk_tree_view_get_headers_clickable (tree_view)); */ - break; - case PROP_EXPANDER_COLUMN: - g_value_set_uint (value, tree_view->priv->expander_column); - break; - case PROP_RULES_HINT: - g_value_set_boolean (value, tree_view->priv->has_rules); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; + GtkTreeViewChild *child = tmp_list->data; + tmp_list = tmp_list->next; + + if (GTK_WIDGET_VISIBLE (child->widget)) + { + if (!GTK_WIDGET_MAPPED (child->widget)) + gtk_widget_map (child->widget); + } } -} + gdk_window_show (tree_view->priv->bin_window); -/* Widget methods - */ + gtk_tree_view_map_buttons (tree_view); + + gdk_window_show (widget->window); +} static void gtk_tree_view_realize (GtkWidget *widget) @@ -855,80 +941,15 @@ gtk_tree_view_unrealize (GtkWidget *widget) (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget); } +/* GtkWidget::size_request helper */ static void -gtk_tree_view_map_buttons (GtkTreeView *tree_view) +gtk_tree_view_size_request_buttons (GtkTreeView *tree_view) { GList *list; - g_return_if_fail (GTK_WIDGET_MAPPED (tree_view)); + tree_view->priv->header_height = 1; - if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE)) - { - GtkTreeViewColumn *column; - - for (list = tree_view->priv->columns; list; list = list->next) - { - column = list->data; - if (GTK_WIDGET_VISIBLE (column->button) && - !GTK_WIDGET_MAPPED (column->button)) - gtk_widget_map (column->button); - } - for (list = tree_view->priv->columns; list; list = list->next) - { - column = list->data; - if (column->visible == FALSE) - continue; - if (column->column_type == GTK_TREE_VIEW_COLUMN_RESIZEABLE) - { - gdk_window_raise (column->window); - gdk_window_show (column->window); - } - else - gdk_window_hide (column->window); - } - gdk_window_show (tree_view->priv->header_window); - } -} - -static void -gtk_tree_view_map (GtkWidget *widget) -{ - GList *tmp_list; - GtkTreeView *tree_view; - - g_return_if_fail (GTK_IS_TREE_VIEW (widget)); - - tree_view = GTK_TREE_VIEW (widget); - - GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED); - - tmp_list = tree_view->priv->children; - while (tmp_list) - { - GtkTreeViewChild *child = tmp_list->data; - tmp_list = tmp_list->next; - - if (GTK_WIDGET_VISIBLE (child->widget)) - { - if (!GTK_WIDGET_MAPPED (child->widget)) - gtk_widget_map (child->widget); - } - } - gdk_window_show (tree_view->priv->bin_window); - - gtk_tree_view_map_buttons (tree_view); - - gdk_window_show (widget->window); -} - -static void -gtk_tree_view_size_request_buttons (GtkTreeView *tree_view) -{ - GList *list; - - tree_view->priv->header_height = 1; - - if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_MODEL_SETUP)) + if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_MODEL_SETUP)) { for (list = tree_view->priv->columns; list; list = list->next) { @@ -975,6 +996,7 @@ gtk_tree_view_size_request (GtkWidget *widget, gtk_tree_view_size_request_buttons (tree_view); } +/* GtkWidget::size_allocate helper */ static void gtk_tree_view_size_allocate_buttons (GtkWidget *widget) { @@ -1086,563 +1108,417 @@ gtk_tree_view_size_allocate (GtkWidget *widget, } -GdkPixmap* -gtk_tree_view_create_row_drag_icon (GtkTreeView *tree_view, - GtkTreePath *path) +static gboolean +gtk_tree_view_button_press (GtkWidget *widget, + GdkEventButton *event) { - GtkTreeIter iter; - GtkRBTree *tree; - GtkRBNode *node; - GtkCellRenderer *cell; - gint i; - gint cell_offset; + GtkTreeView *tree_view; GList *list; + GtkTreeViewColumn *column = NULL; + gint i; GdkRectangle background_area; - GtkWidget *widget; - gint depth; - /* start drawing inside the black outline */ - gint x = 1, y = 1; - GdkDrawable *drawable; - gint bin_window_width; + GdkRectangle cell_area; + gint vertical_separator; - widget = GTK_WIDGET (tree_view); + g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); - depth = gtk_tree_path_get_depth (path); + tree_view = GTK_TREE_VIEW (widget); + gtk_widget_style_get (widget, "vertical_separator", &vertical_separator, NULL); - _gtk_tree_view_find_node (tree_view, - path, - &tree, - &node); + if (event->window == tree_view->priv->bin_window) + { + GtkRBNode *node; + GtkRBTree *tree; + GtkTreePath *path; + gchar *path_string; + gint depth; + gint new_y; + gint y_offset; + GtkTreeViewColumn *column = NULL; - if (tree == NULL) - return NULL; + if (!GTK_WIDGET_HAS_FOCUS (widget)) + gtk_widget_grab_focus (widget); + GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS); - if (!gtk_tree_model_get_iter (tree_view->priv->model, - &iter, - path)) - return NULL; + /* are we in an arrow? */ + if (tree_view->priv->prelight_node && + GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT)) + { + if (event->button == 1) + { + gtk_grab_add (widget); + tree_view->priv->button_pressed_node = tree_view->priv->prelight_node; + tree_view->priv->button_pressed_tree = tree_view->priv->prelight_tree; + gtk_tree_view_draw_arrow (GTK_TREE_VIEW (widget), + tree_view->priv->prelight_tree, + tree_view->priv->prelight_node, + event->x, + event->y); + } + return TRUE; + } - cell_offset = x; + /* find the node that was clicked */ + new_y = ((gint)event->yy; + y_offset = -_gtk_rbtree_find_offset (tree_view->priv->tree, + TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, new_y), + &tree, + &node) + new_y - (gint)event->y; - background_area.y = y; - background_area.height = BACKGROUND_HEIGHT (node); + if (node == NULL) + /* We clicked in dead space */ + return TRUE; - gdk_drawable_get_size (tree_view->priv->bin_window, - &bin_window_width, NULL); + /* Get the path and the node */ + path = _gtk_tree_view_find_path (tree_view, tree, node); + depth = gtk_tree_path_get_depth (path); + background_area.y = y_offset + event->y + vertical_separator; + background_area.height = GTK_RBNODE_GET_HEIGHT (node) - vertical_separator; + background_area.x = 0; + /* Let the cell have a chance at selecting it. */ - drawable = gdk_pixmap_new (tree_view->priv->bin_window, - bin_window_width + 2, - background_area.height + 2, - -1); + for (i = 0, list = tree_view->priv->columns; i < tree_view->priv->n_columns; i++, list = list->next) + { + GtkCellRenderer *cell; + GtkTreeIter iter; + gboolean visible; + gboolean can_activate; - gdk_draw_rectangle (drawable, - widget->style->base_gc[GTK_WIDGET_STATE (widget)], - TRUE, - 0, 0, - bin_window_width + 2, - background_area.height + 2); + column = list->data; - gdk_draw_rectangle (drawable, - widget->style->black_gc, - FALSE, - 0, 0, - bin_window_width + 1, - background_area.height + 1); + if (!column->visible) + continue; - for (i = 0, list = tree_view->priv->columns; i < tree_view->priv->n_columns; i++, list = list->next) - { - GtkTreeViewColumn *column = list->data; - GdkRectangle cell_area; - gboolean visible; - gint vertical_separator; + background_area.width = column->displayed_width; + if (i == tree_view->priv->expander_column && + TREE_VIEW_DRAW_EXPANDERS(tree_view)) + { + cell_area = background_area; + cell_area.x += depth*tree_view->priv->tab_offset; + cell_area.width -= depth*tree_view->priv->tab_offset; + } + else + { + cell_area = background_area; + } - if (!column->visible) - continue; + cell = column->cell; - cell = column->cell; - gtk_tree_view_column_set_cell_data (column, - tree_view->priv->model, - &iter); + if ((background_area.x > (gint) event->x) || + (background_area.y > (gint) event->y) || + (background_area.x + background_area.width <= (gint) event->x) || + (background_area.y + background_area.height <= (gint) event->y)) + { + background_area.x += background_area.width; + continue; + } - background_area.x = cell_offset; - background_area.width = column->displayed_width; + gtk_tree_model_get_iter (tree_view->priv->model, + &iter, + path); + gtk_tree_view_column_set_cell_data (column, + tree_view->priv->model, + &iter); - cell_area = background_area; + path_string = gtk_tree_path_to_string (path); - gtk_widget_style_get (widget, "vertical_separator", &vertical_separator, NULL); - cell_area.y += vertical_separator / 2; - cell_area.height -= vertical_separator; + g_object_get (G_OBJECT (cell), + "visible", &visible, + "can_activate", &can_activate, + NULL); + if (visible && + can_activate && + gtk_cell_renderer_event (cell, (GdkEvent *)event, + widget, path_string, + &background_area, + &cell_area, 0)) + { + g_free (path_string); + gtk_tree_path_free (path); + return TRUE; + } + else + { + g_free (path_string); + break; + } + } - if (i == tree_view->priv->expander_column && - TREE_VIEW_DRAW_EXPANDERS(tree_view)) - { - cell_area.x += depth * tree_view->priv->tab_offset; - cell_area.width -= depth * tree_view->priv->tab_offset; - } + if (column == NULL) + return FALSE; - g_object_get (G_OBJECT (cell), "visible", &visible, NULL); - if (visible) - gtk_cell_renderer_render (cell, - drawable, - widget, - &background_area, - &cell_area, - NULL, - 0); + /* Save press to possibly begin a drag + */ + if (tree_view->priv->pressed_button < 0) + { + tree_view->priv->pressed_button = event->button; + tree_view->priv->press_start_x = event->x; + tree_view->priv->press_start_y = event->y; + } - cell_offset += column->displayed_width; + /* Handle the selection */ + if (tree_view->priv->selection == NULL) + tree_view->priv->selection = + _gtk_tree_selection_new_with_tree_view (tree_view); + + _gtk_tree_selection_internal_select_node (tree_view->priv->selection, + node, + tree, + path, + event->state); + + if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) + gtk_tree_view_row_activated (tree_view, path, column); + + gtk_tree_path_free (path); + return TRUE; } - return drawable; + for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++) + { + column = list->data; + if (event->window == column->window && + column->column_type == GTK_TREE_VIEW_COLUMN_RESIZEABLE && + column->window) + { + gpointer drag_data; + + if (gdk_pointer_grab (column->window, FALSE, + GDK_POINTER_MOTION_HINT_MASK | + GDK_BUTTON1_MOTION_MASK | + GDK_BUTTON_RELEASE_MASK, + NULL, NULL, event->time)) + return FALSE; + + gtk_grab_add (widget); + GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE); + + /* block attached dnd signal handler */ + drag_data = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data"); + if (drag_data) + gtk_signal_handler_block_by_data (GTK_OBJECT (widget), drag_data); + + if (!GTK_WIDGET_HAS_FOCUS (widget)) + gtk_widget_grab_focus (widget); + + tree_view->priv->drag_pos = i; + tree_view->priv->x_drag = (column->button->allocation.x + column->button->allocation.width); + } + } + return TRUE; } -/* Warning: Very scary function. - * Modify at your own risk - */ +/* GtkWidget::button_release_event helper */ static gboolean -gtk_tree_view_bin_expose (GtkWidget *widget, - GdkEventExpose *event) +gtk_tree_view_button_release_drag_column (GtkWidget *widget, + GdkEventButton *event) { GtkTreeView *tree_view; - GtkTreePath *path; - GtkRBTree *tree; - GList *list; - GtkRBNode *node; - GtkRBNode *cursor = NULL; - GtkRBTree *cursor_tree = NULL; - GtkRBNode *drag_highlight = NULL; - GtkRBTree *drag_highlight_tree = NULL; - GtkTreeIter iter; - GtkCellRenderer *cell; - gint new_y; - gint y_offset, x_offset, cell_offset; - gint i, max_height; - gint depth; - GdkRectangle background_area; - GdkRectangle cell_area; - guint flags; - gint highlight_x; - gint bin_window_width; - GtkTreePath *cursor_path; - GtkTreePath *drag_dest_path; - GList *last_column; - gint vertical_separator; - - g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE); + GtkAllocation allocation; tree_view = GTK_TREE_VIEW (widget); - gtk_widget_style_get (widget, "vertical_separator", &vertical_separator, NULL); - if (tree_view->priv->tree == NULL) - return TRUE; + allocation = tree_view->priv->drag_column->button->allocation; + allocation.x = tree_view->priv->drag_column_x; + gdk_pointer_ungrab (GDK_CURRENT_TIME); + gdk_window_reparent (tree_view->priv->drag_column->button->window, + tree_view->priv->header_window, + tree_view->priv->drag_column_x, + tree_view->priv->drag_column->button->allocation.y); + gtk_widget_set_parent_window (tree_view->priv->drag_column->button, tree_view->priv->header_window); + gtk_widget_size_allocate (tree_view->priv->drag_column->button, &allocation); - gtk_tree_view_check_dirty (GTK_TREE_VIEW (widget)); - /* we want to account for a potential HEADER offset. - * That is, if the header exists, we want to offset our event by its - * height to find the right node. - */ - new_y = (event->area.yarea.y; + tree_view->priv->drag_column = NULL; + gdk_window_hide (tree_view->priv->drag_window); - /* y_offset is the */ + g_list_foreach (tree_view->priv->column_drag_info, (GFunc) g_free, NULL); + g_list_free (tree_view->priv->column_drag_info); + tree_view->priv->column_drag_info = NULL; - y_offset = -_gtk_rbtree_find_offset (tree_view->priv->tree, - TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, new_y), - &tree, - &node) + new_y - event->area.y; - if (node == NULL) - return TRUE; + gdk_window_hide (tree_view->priv->drag_highlight_window); - /* find the path for the node */ - path = _gtk_tree_view_find_path ((GtkTreeView *)widget, - tree, - node); - gtk_tree_model_get_iter (tree_view->priv->model, - &iter, - path); - depth = gtk_tree_path_get_depth (path); - gtk_tree_path_free (path); + /* deal with headers */ + gdk_window_reparent (tree_view->priv->header_window, + widget->window, + 0, 0); + gdk_window_hide (tree_view->priv->drag_header_window); - cursor_path = NULL; - drag_dest_path = NULL; + return TRUE; +} - if (tree_view->priv->cursor) - cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); +/* GtkWidget::button_release_event helper */ +static gboolean +gtk_tree_view_button_release_column_resize (GtkWidget *widget, + GdkEventButton *event) +{ + GtkTreeView *tree_view; + gpointer drag_data; + gint width; + gint x; + gint i; - if (cursor_path) - _gtk_tree_view_find_node (tree_view, cursor_path, - &cursor_tree, &cursor); + tree_view = GTK_TREE_VIEW (widget); - if (tree_view->priv->drag_dest_row) - drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row); + i = tree_view->priv->drag_pos; + tree_view->priv->drag_pos = -1; - if (drag_dest_path) - _gtk_tree_view_find_node (tree_view, drag_dest_path, - &drag_highlight_tree, &drag_highlight); + /* unblock attached dnd signal handler */ + drag_data = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data"); + if (drag_data) + gtk_signal_handler_unblock_by_data (GTK_OBJECT (widget), drag_data); - gdk_drawable_get_size (tree_view->priv->bin_window, - &bin_window_width, NULL); + GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE); + gtk_widget_get_pointer (widget, &x, NULL); + gtk_grab_remove (widget); + gdk_pointer_ungrab (event->time); - for (last_column = g_list_last (tree_view->priv->columns); - last_column && - !(GTK_TREE_VIEW_COLUMN (last_column->data)->visible) && - GTK_WIDGET_CAN_FOCUS (GTK_TREE_VIEW_COLUMN (last_column->data)->button); - last_column = last_column->prev) - ; + width = gtk_tree_view_new_column_width (GTK_TREE_VIEW (widget), i, &x); + gtk_tree_view_column_set_width (gtk_tree_view_get_column (GTK_TREE_VIEW (widget), i), width); - /* Actually process the expose event. To do this, we want to - * start at the first node of the event, and walk the tree in - * order, drawing each successive node. - */ + return TRUE; +} - do - { - /* Need to think about this more. - if (tree_view->priv->show_expanders) - max_height = MAX (TREE_VIEW_EXPANDER_MIN_HEIGHT, GTK_RBNODE_GET_HEIGHT (node)); - else - */ - gboolean parity; +static gboolean +gtk_tree_view_button_release (GtkWidget *widget, + GdkEventButton *event) +{ + GtkTreeView *tree_view; - max_height = BACKGROUND_HEIGHT (node); + g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); - x_offset = -event->area.x; - cell_offset = 0; - highlight_x = 0; /* should match x coord of first cell */ + tree_view = GTK_TREE_VIEW (widget); - background_area.y = y_offset + event->area.y; - background_area.height = max_height; - flags = 0; + if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_DRAG)) + return gtk_tree_view_button_release_drag_column (widget, event); - if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PRELIT)) - flags |= GTK_CELL_RENDERER_PRELIT; + if (tree_view->priv->pressed_button == event->button) + tree_view->priv->pressed_button = -1; - parity = _gtk_rbtree_node_find_parity (tree, node); + if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE)) + return gtk_tree_view_button_release_column_resize (widget, event); - if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED)) - flags |= GTK_CELL_RENDERER_SELECTED; + if (tree_view->priv->button_pressed_node == NULL) + return FALSE; - for (i = 0, list = tree_view->priv->columns; i < tree_view->priv->n_columns; i++, list = list->next) + if (event->button == 1) + { + gtk_grab_remove (widget); + if (tree_view->priv->button_pressed_node == tree_view->priv->prelight_node && + GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT)) { - GtkTreeViewColumn *column = list->data; - const gchar *detail = NULL; - - if (!column->visible) - continue; + GtkTreePath *path = NULL; + GtkTreeIter iter; - if (cell_offset > event->area.x + event->area.width || - cell_offset + column->displayed_width < event->area.x) + /* Actually activate the node */ + if (tree_view->priv->button_pressed_node->children == NULL) { - cell_offset += column->displayed_width; - continue; - } - - if (column->show_sort_indicator) - flags |= GTK_CELL_RENDERER_SORTED; - else - flags &= ~GTK_CELL_RENDERER_SORTED; - - cell = column->cell; - gtk_tree_view_column_set_cell_data (column, - tree_view->priv->model, - &iter); - - background_area.x = cell_offset; - background_area.width = column->displayed_width; - - cell_area = background_area; - cell_area.y += vertical_separator / 2; - cell_area.height -= vertical_separator; + GtkTreeIter child; + path = _gtk_tree_view_find_path (tree_view, + tree_view->priv->button_pressed_tree, + tree_view->priv->button_pressed_node); + gtk_tree_model_get_iter (tree_view->priv->model, &iter, path); - /* Select the detail for drawing the cell. relevant - * factors are parity, sortedness, and whether to - * display rules. - */ - - /* FIXME when we have style properties, clean this up. - */ - - if (tree_view->priv->has_rules) - { - if (flags & GTK_CELL_RENDERER_SORTED) - { - if (parity) - detail = "cell_odd_ruled_sorted"; - else - detail = "cell_even_ruled_sorted"; - } - else - { - if (parity) - detail = "cell_odd_ruled"; - else - detail = "cell_even_ruled"; - } - } - else - { - if (flags & GTK_CELL_RENDERER_SORTED) - { - if (parity) - detail = "cell_odd_sorted"; - else - detail = "cell_even_sorted"; - } - else - { - if (parity) - detail = "cell_odd"; - else - detail = "cell_even"; - } - } - - g_assert (detail); - - /* Draw background */ - gtk_paint_flat_box (widget->style, - event->window, - (flags & GTK_CELL_RENDERER_SELECTED) ? - GTK_STATE_SELECTED : GTK_STATE_NORMAL, - GTK_SHADOW_NONE, - &event->area, - widget, - detail, - background_area.x, - background_area.y, - background_area.width, - background_area.height); - - if (i == tree_view->priv->expander_column && - TREE_VIEW_DRAW_EXPANDERS(tree_view)) - { - gboolean visible; - - cell_area.x += depth*tree_view->priv->tab_offset; - cell_area.width -= depth*tree_view->priv->tab_offset; - - /* If we have an expander column, the highlight underline - * starts with that column, so that it indicates which - * level of the tree we're dropping at. - */ - highlight_x = cell_area.x; - - g_object_get (G_OBJECT (cell), "visible", &visible, NULL); - if (visible) - gtk_cell_renderer_render (cell, - event->window, - widget, - &background_area, - &cell_area, - &event->area, - flags); - - if ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT) + if (gtk_tree_model_iter_children (tree_view->priv->model, &child, &iter)) { - gint x, y; - gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, 0); - gtk_tree_view_draw_arrow (GTK_TREE_VIEW (widget), - tree, - node, - x, y); + gboolean expand; + g_signal_emit (G_OBJECT (tree_view), tree_view_signals[EXPAND_ROW], 0, &iter, path, &expand); + if (! expand) + { + tree_view->priv->button_pressed_node->children = _gtk_rbtree_new (); + tree_view->priv->button_pressed_node->children->parent_tree = tree_view->priv->button_pressed_tree; + tree_view->priv->button_pressed_node->children->parent_node = tree_view->priv->button_pressed_node; + gtk_tree_view_build_tree (tree_view, + tree_view->priv->button_pressed_node->children, + &child, + gtk_tree_path_get_depth (path) + 1, + FALSE, + GTK_WIDGET_REALIZED (widget)); + } } } else { - gboolean visible; - g_object_get (G_OBJECT (cell), "visible", &visible, NULL); + gboolean collapse; - if (visible) - gtk_cell_renderer_render (cell, - event->window, - widget, - &background_area, - &cell_area, - &event->area, - flags); + path = _gtk_tree_view_find_path (GTK_TREE_VIEW (widget), + tree_view->priv->button_pressed_tree, + tree_view->priv->button_pressed_node); + gtk_tree_model_get_iter (tree_view->priv->model, + &iter, + path); + g_signal_emit (G_OBJECT (tree_view), tree_view_signals[COLLAPSE_ROW], 0, &iter, path, &collapse); + + if (! collapse) + { + GtkTreeIter child_iter; + gtk_tree_path_append_index (path, 0); + gtk_tree_model_iter_children (tree_view->priv->model, + &child_iter, + &iter); + gtk_tree_view_discover_dirty (GTK_TREE_VIEW (widget), + tree_view->priv->button_pressed_node->children, + &child_iter, + gtk_tree_path_get_depth (path)); + gtk_tree_view_unref_tree (GTK_TREE_VIEW (widget), + tree_view->priv->button_pressed_node->children); + _gtk_rbtree_remove (tree_view->priv->button_pressed_node->children); + } } - cell_offset += column->displayed_width; - } + if (path) + gtk_tree_path_free (path); - if (node == cursor && GTK_WIDGET_HAS_FOCUS (widget)) - gtk_tree_view_draw_focus (widget); + _gtk_tree_view_update_size (GTK_TREE_VIEW (widget)); + } - if (node == drag_highlight) - { - /* Draw indicator for the drop - */ - gint highlight_y = -1; - GtkRBTree *tree = NULL; - GtkRBNode *node = NULL; - gint width; + tree_view->priv->button_pressed_node = NULL; + } - switch (tree_view->priv->drag_dest_pos) - { - case GTK_TREE_VIEW_DROP_BEFORE: - highlight_y = background_area.y - vertical_separator/2; - break; + return TRUE; +} - case GTK_TREE_VIEW_DROP_AFTER: - highlight_y = background_area.y + background_area.height + vertical_separator/2; - break; +static gboolean +coords_are_over_arrow (GtkTreeView *tree_view, + GtkRBTree *tree, + GtkRBNode *node, + /* these are in tree window coords */ + gint x, + gint y) +{ + GdkRectangle arrow; + gint x2; - case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: - case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: - _gtk_tree_view_find_node (tree_view, drag_dest_path, &tree, &node); + if (!GTK_WIDGET_REALIZED (tree_view)) + return FALSE; - if (tree == NULL) - break; - gdk_drawable_get_size (tree_view->priv->bin_window, - &width, NULL); - gtk_paint_focus (widget->style, - tree_view->priv->bin_window, - NULL, - widget, - "add-mode", - 0, BACKGROUND_FIRST_PIXEL (tree_view, tree, node), - width - 1, BACKGROUND_HEIGHT (node) - 1); + if ((node->flags & GTK_RBNODE_IS_PARENT) == 0) + return FALSE; - break; - } + arrow.y = BACKGROUND_FIRST_PIXEL (tree_view, tree, node); - if (highlight_y >= 0) - { - gdk_draw_line (event->window, - widget->style->black_gc, - highlight_x, - highlight_y, - bin_window_width - highlight_x, - highlight_y); - } - } + arrow.height = BACKGROUND_HEIGHT (node); - y_offset += max_height; - if (node->children) - { - GtkTreeIter parent = iter; - gboolean has_child; + gtk_tree_view_get_arrow_xrange (tree_view, &arrow.x, &x2); - tree = node->children; - node = tree->root; + arrow.width = x2 - arrow.x; - g_assert (node != tree->nil); + return (x >= arrow.x && + x < (arrow.x + arrow.height) && + y >= arrow.y && + y < (arrow.y + arrow.height)); +} - while (node->left != tree->nil) - node = node->left; - has_child = gtk_tree_model_iter_children (tree_view->priv->model, - &iter, - &parent); - cell = gtk_tree_view_get_column (tree_view, 0)->cell; - depth++; +static void +do_unprelight (GtkTreeView *tree_view, + /* these are in tree window coords */ + gint x, + gint y) +{ + if (tree_view->priv->prelight_node == NULL) + return; - /* Sanity Check! */ - TREE_VIEW_INTERNAL_ASSERT (has_child, FALSE); - } - else - { - gboolean done = FALSE; - do - { - node = _gtk_rbtree_next (tree, node); - if (node != NULL) - { - gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter); - cell = gtk_tree_view_get_column (tree_view, 0)->cell; - done = TRUE; - - /* Sanity Check! */ - TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE); - } - else - { - GtkTreeIter parent_iter = iter; - gboolean has_parent; - - node = tree->parent_node; - tree = tree->parent_tree; - if (tree == NULL) - /* we've run out of tree. It's okay to return though, as - * we'd only break out of the while loop below. */ - return TRUE; - has_parent = gtk_tree_model_iter_parent (tree_view->priv->model, - &iter, - &parent_iter); - depth--; - - /* Sanity check */ - TREE_VIEW_INTERNAL_ASSERT (has_parent, FALSE); - } - } - while (!done); - } - } - while (y_offset < event->area.height); - - if (cursor_path) - gtk_tree_path_free (cursor_path); - - if (drag_dest_path) - gtk_tree_path_free (drag_dest_path); - - return TRUE; -} - -static gboolean -gtk_tree_view_expose (GtkWidget *widget, - GdkEventExpose *event) -{ - GtkTreeView *tree_view; - - g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE); - - tree_view = GTK_TREE_VIEW (widget); - - if (event->window == tree_view->priv->bin_window) - return gtk_tree_view_bin_expose (widget, event); - - return TRUE; -} - -static gboolean -coords_are_over_arrow (GtkTreeView *tree_view, - GtkRBTree *tree, - GtkRBNode *node, - /* these are in tree window coords */ - gint x, - gint y) -{ - GdkRectangle arrow; - gint x2; - - if (!GTK_WIDGET_REALIZED (tree_view)) - return FALSE; - - if ((node->flags & GTK_RBNODE_IS_PARENT) == 0) - return FALSE; - - arrow.y = BACKGROUND_FIRST_PIXEL (tree_view, tree, node); - - arrow.height = BACKGROUND_HEIGHT (node); - - gtk_tree_view_get_arrow_xrange (tree_view, &arrow.x, &x2); - - arrow.width = x2 - arrow.x; - - return (x >= arrow.x && - x < (arrow.x + arrow.height) && - y >= arrow.y && - y < (arrow.y + arrow.height)); -} - -static void -do_unprelight (GtkTreeView *tree_view, - /* these are in tree window coords */ - gint x, - gint y) -{ - if (tree_view->priv->prelight_node == NULL) - return; - - GTK_RBNODE_UNSET_FLAG (tree_view->priv->prelight_node, GTK_RBNODE_IS_PRELIT); + GTK_RBNODE_UNSET_FLAG (tree_view->priv->prelight_node, GTK_RBNODE_IS_PRELIT); if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT) && !coords_are_over_arrow (tree_view, @@ -1689,7 +1565,7 @@ ensure_unprelighted (GtkTreeView *tree_view) do_unprelight (tree_view, -1000, -1000); /* coords not possibly over an arrow */ } -/* Motion Event */ +/* GtkWidget::motion_event helper */ static gboolean gtk_tree_view_motion_resize_column (GtkWidget *widget, GdkEventMotion *event) @@ -1725,7 +1601,7 @@ gtk_tree_view_motion_resize_column (GtkWidget *widget, * 00000111100000 * 00000111100000 * 00000111100000 - * ~ ~ ~ ~ ~ ~ ~ + * ~ ~ ~ ~ ~ ~ ~ * 00000111100000 * 00000111100000 * 00000111100000 @@ -1834,7 +1710,56 @@ gtk_tree_view_motion_draw_column_motion_arrow (GtkTreeView *tree_view) gdk_window_show (tree_view->priv->drag_highlight_window); gdk_window_raise (tree_view->priv->drag_highlight_window); } - + +static gint +drag_column_scroll_timeout (gpointer data) +{ + GtkTreeView *tree_view; + gint x, y; + GdkModifierType state; + GtkTreePath *path = NULL; + GtkTreeViewColumn *column = NULL; + GdkRectangle visible_rect; + + tree_view = GTK_TREE_VIEW (data); + + gdk_window_get_pointer (tree_view->priv->bin_window, + &x, &y, &state); + + gtk_tree_view_get_visible_rect (tree_view, &visible_rect); + + /* See if we are near the edge. */ + if ((x - visible_rect.x) < SCROLL_EDGE_SIZE || + (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE) + { + gtk_tree_view_get_path_at_pos (tree_view, + tree_view->priv->bin_window, + x, y, + &path, + &column, + NULL, + NULL); + + if (path != NULL) + { + gtk_tree_view_scroll_to_cell (tree_view, + path, + column, + 0.5, 0.5); + + gtk_tree_path_free (path); + } + } + + return TRUE; +} + +static void +ensure_drag_column_scroll_timeout (GtkTreeView *tree_view) +{ + if (tree_view->priv->scroll_timeout == 0) + tree_view->priv->scroll_timeout = gtk_timeout_add (50, drag_column_scroll_timeout, tree_view); +} static gboolean gtk_tree_view_motion_drag_column (GtkWidget *widget, @@ -1852,12 +1777,14 @@ gtk_tree_view_motion_drag_column (GtkWidget *widget, if (event->window != tree_view->priv->drag_window) return FALSE; + ensure_drag_column_scroll_timeout (tree_view); + gdk_window_get_position (tree_view->priv->drag_window, &x, &y); x = CLAMP (x + (gint)event->x - column->drag_x, 0, MAX (tree_view->priv->width, GTK_WIDGET (tree_view)->allocation.width) - column->button->allocation.width); gdk_window_move (tree_view->priv->drag_window, x, y); - + gdk_window_get_position (tree_view->priv->drag_window, &x, NULL); x += (gint)event->x; for (list = tree_view->priv->column_drag_info; list; list = list->next) @@ -1868,94 +1795,588 @@ gtk_tree_view_motion_drag_column (GtkWidget *widget, reorder = NULL; } - if (reorder && reorder == tree_view->priv->cur_reorder) - return TRUE; + if (reorder && reorder == tree_view->priv->cur_reorder) + return TRUE; + + tree_view->priv->cur_reorder = reorder; + + gtk_tree_view_motion_draw_column_motion_arrow (tree_view); + + return TRUE; +} + +static gboolean +gtk_tree_view_motion_bin_window (GtkWidget *widget, + GdkEventMotion *event) +{ + GtkTreeView *tree_view; + GtkRBTree *tree; + GtkRBNode *node; + gint new_y; + GtkRBTree *old_prelight_tree; + GtkRBNode *old_prelight_node; + + tree_view = (GtkTreeView *) widget; + + if (tree_view->priv->tree == NULL) + return FALSE; + + gtk_tree_view_maybe_begin_dragging_row (tree_view, event); + + old_prelight_tree = tree_view->priv->prelight_tree; + old_prelight_node = tree_view->priv->prelight_node; + + do_unprelight (tree_view, event->x, event->y); + + new_y = ((gint)event->yy; + + _gtk_rbtree_find_offset (tree_view->priv->tree, + TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, new_y), + &tree, + &node); + + if (tree == NULL) + return TRUE; + + /* If we are currently pressing down a button, we don't want to prelight anything else. */ + if ((tree_view->priv->button_pressed_node != NULL) && + (tree_view->priv->button_pressed_node != node)) + return TRUE; + + + do_prelight (tree_view, tree, node, event->x, new_y); + + if (old_prelight_node != tree_view->priv->prelight_node) + { + if (old_prelight_node) + gtk_tree_view_queue_draw_node (tree_view, + old_prelight_tree, + old_prelight_node, + NULL); + + if (tree_view->priv->prelight_node) + gtk_tree_view_queue_draw_node (tree_view, + tree_view->priv->prelight_tree, + tree_view->priv->prelight_node, + NULL); + } + + return TRUE; +} + +static gboolean +gtk_tree_view_motion (GtkWidget *widget, + GdkEventMotion *event) +{ + GtkTreeView *tree_view; + + tree_view = (GtkTreeView *) widget; + + if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE)) + return gtk_tree_view_motion_resize_column (widget, event); + + /* Drag column */ + if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_DRAG)) + return gtk_tree_view_motion_drag_column (widget, event); + + /* Sanity check it */ + if (event->window == tree_view->priv->bin_window) + return gtk_tree_view_motion_bin_window (widget, event); + return FALSE; +} + + +static void +gtk_tree_view_draw_focus (GtkWidget *widget) +{ + GtkTreeView *tree_view; + GtkTreePath *cursor_path; + GtkRBTree *tree = NULL; + GtkRBNode *node = NULL; + gint x, y; + gint width, height; + gint vertical_separator; + + g_return_if_fail (GTK_IS_TREE_VIEW (widget)); + + tree_view = GTK_TREE_VIEW (widget); + + gtk_widget_style_get (widget, "vertical_separator", &vertical_separator, NULL); + + if (! GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS)) + return; + + if (tree_view->priv->cursor == NULL) + return; + + cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); + if (cursor_path == NULL) + return; + + _gtk_tree_view_find_node (tree_view, cursor_path, &tree, &node); + + if (tree == NULL) + { + gtk_tree_path_free (cursor_path); + return; + } + + gdk_drawable_get_size (tree_view->priv->bin_window, + &width, NULL); + + + x = 0; + y = BACKGROUND_FIRST_PIXEL (tree_view, tree, node); + gdk_drawable_get_size (tree_view->priv->bin_window, + &width, NULL); + width = width - 1; + height = BACKGROUND_HEIGHT (node) - 1; + if (tree_view->priv->focus_column != NULL) + { + gboolean visible; + gboolean can_focus; + + g_object_get (G_OBJECT (tree_view->priv->focus_column->cell), + "can_activate", &can_focus, + "visible", &visible, + NULL); + if (can_focus && visible) + { + GtkTreeIter iter; + GdkRectangle cell_area; + gint x_offset; + gint y_offset; + + cell_area.x = tree_view->priv->focus_column->button->allocation.x; + cell_area.y = y; + cell_area.width = tree_view->priv->focus_column->displayed_width; + cell_area.height = CELL_HEIGHT (node, vertical_separator); + + gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path); + gtk_tree_view_column_set_cell_data (tree_view->priv->focus_column, tree_view->priv->model, &iter); + + gtk_cell_renderer_get_size (tree_view->priv->focus_column->cell, GTK_WIDGET (tree_view), &cell_area, &x_offset, &y_offset, &width, &height); + width += 2; + height += 2; + x = cell_area.x + x_offset - 1; + y = cell_area.y + y_offset - 1 + vertical_separator/2; + } + } + + gtk_paint_focus (widget->style, + tree_view->priv->bin_window, + NULL, + widget, + "add-mode", + x, y, width, height); + + gtk_tree_path_free (cursor_path); +} + +/* Warning: Very scary function. + * Modify at your own risk + */ +static gboolean +gtk_tree_view_bin_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + GtkTreeView *tree_view; + GtkTreePath *path; + GtkRBTree *tree; + GList *list; + GtkRBNode *node; + GtkRBNode *cursor = NULL; + GtkRBTree *cursor_tree = NULL; + GtkRBNode *drag_highlight = NULL; + GtkRBTree *drag_highlight_tree = NULL; + GtkTreeIter iter; + GtkCellRenderer *cell; + gint new_y; + gint y_offset, x_offset, cell_offset; + gint i, max_height; + gint depth; + GdkRectangle background_area; + GdkRectangle cell_area; + guint flags; + gint highlight_x; + gint bin_window_width; + GtkTreePath *cursor_path; + GtkTreePath *drag_dest_path; + GList *last_column; + gint vertical_separator; + + g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE); + + tree_view = GTK_TREE_VIEW (widget); + gtk_widget_style_get (widget, "vertical_separator", &vertical_separator, NULL); + + if (tree_view->priv->tree == NULL) + return TRUE; + + gtk_tree_view_check_dirty (GTK_TREE_VIEW (widget)); + /* we want to account for a potential HEADER offset. + * That is, if the header exists, we want to offset our event by its + * height to find the right node. + */ + new_y = (event->area.yarea.y; + + /* y_offset is the */ + + y_offset = -_gtk_rbtree_find_offset (tree_view->priv->tree, + TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, new_y), + &tree, + &node) + new_y - event->area.y; + if (node == NULL) + return TRUE; + + /* find the path for the node */ + path = _gtk_tree_view_find_path ((GtkTreeView *)widget, + tree, + node); + gtk_tree_model_get_iter (tree_view->priv->model, + &iter, + path); + depth = gtk_tree_path_get_depth (path); + gtk_tree_path_free (path); + + cursor_path = NULL; + drag_dest_path = NULL; + + if (tree_view->priv->cursor) + cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); + + if (cursor_path) + _gtk_tree_view_find_node (tree_view, cursor_path, + &cursor_tree, &cursor); + + if (tree_view->priv->drag_dest_row) + drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row); + + if (drag_dest_path) + _gtk_tree_view_find_node (tree_view, drag_dest_path, + &drag_highlight_tree, &drag_highlight); + + gdk_drawable_get_size (tree_view->priv->bin_window, + &bin_window_width, NULL); + + for (last_column = g_list_last (tree_view->priv->columns); + last_column && + !(GTK_TREE_VIEW_COLUMN (last_column->data)->visible) && + GTK_WIDGET_CAN_FOCUS (GTK_TREE_VIEW_COLUMN (last_column->data)->button); + last_column = last_column->prev) + ; + + /* Actually process the expose event. To do this, we want to + * start at the first node of the event, and walk the tree in + * order, drawing each successive node. + */ + + do + { + /* Need to think about this more. + if (tree_view->priv->show_expanders) + max_height = MAX (TREE_VIEW_EXPANDER_MIN_HEIGHT, GTK_RBNODE_GET_HEIGHT (node)); + else + */ + gboolean parity; + + max_height = BACKGROUND_HEIGHT (node); + + x_offset = -event->area.x; + cell_offset = 0; + highlight_x = 0; /* should match x coord of first cell */ + + background_area.y = y_offset + event->area.y; + background_area.height = max_height; + flags = 0; + + if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PRELIT)) + flags |= GTK_CELL_RENDERER_PRELIT; + + parity = _gtk_rbtree_node_find_parity (tree, node); + + if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED)) + flags |= GTK_CELL_RENDERER_SELECTED; + + for (i = 0, list = tree_view->priv->columns; i < tree_view->priv->n_columns; i++, list = list->next) + { + GtkTreeViewColumn *column = list->data; + const gchar *detail = NULL; + + if (!column->visible) + continue; + + if (cell_offset > event->area.x + event->area.width || + cell_offset + column->displayed_width < event->area.x) + { + cell_offset += column->displayed_width; + continue; + } + + if (column->show_sort_indicator) + flags |= GTK_CELL_RENDERER_SORTED; + else + flags &= ~GTK_CELL_RENDERER_SORTED; + + cell = column->cell; + gtk_tree_view_column_set_cell_data (column, + tree_view->priv->model, + &iter); + + background_area.x = cell_offset; + background_area.width = column->displayed_width; + + cell_area = background_area; + cell_area.y += vertical_separator / 2; + cell_area.height -= vertical_separator; + + /* Select the detail for drawing the cell. relevant + * factors are parity, sortedness, and whether to + * display rules. + */ + + /* FIXME when we have style properties, clean this up. + */ + + if (tree_view->priv->has_rules) + { + if (flags & GTK_CELL_RENDERER_SORTED) + { + if (parity) + detail = "cell_odd_ruled_sorted"; + else + detail = "cell_even_ruled_sorted"; + } + else + { + if (parity) + detail = "cell_odd_ruled"; + else + detail = "cell_even_ruled"; + } + } + else + { + if (flags & GTK_CELL_RENDERER_SORTED) + { + if (parity) + detail = "cell_odd_sorted"; + else + detail = "cell_even_sorted"; + } + else + { + if (parity) + detail = "cell_odd"; + else + detail = "cell_even"; + } + } + + g_assert (detail); + + /* Draw background */ + gtk_paint_flat_box (widget->style, + event->window, + (flags & GTK_CELL_RENDERER_SELECTED) ? + GTK_STATE_SELECTED : GTK_STATE_NORMAL, + GTK_SHADOW_NONE, + &event->area, + widget, + detail, + background_area.x, + background_area.y, + background_area.width, + background_area.height); + + if (i == tree_view->priv->expander_column && + TREE_VIEW_DRAW_EXPANDERS(tree_view)) + { + gboolean visible; + + cell_area.x += depth*tree_view->priv->tab_offset; + cell_area.width -= depth*tree_view->priv->tab_offset; + + /* If we have an expander column, the highlight underline + * starts with that column, so that it indicates which + * level of the tree we're dropping at. + */ + highlight_x = cell_area.x; + + g_object_get (G_OBJECT (cell), "visible", &visible, NULL); + if (visible) + gtk_cell_renderer_render (cell, + event->window, + widget, + &background_area, + &cell_area, + &event->area, + flags); + + if ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT) + { + gint x, y; + gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, 0); + gtk_tree_view_draw_arrow (GTK_TREE_VIEW (widget), + tree, + node, + x, y); + } + } + else + { + gboolean visible; + g_object_get (G_OBJECT (cell), "visible", &visible, NULL); - tree_view->priv->cur_reorder = reorder; + if (visible) + gtk_cell_renderer_render (cell, + event->window, + widget, + &background_area, + &cell_area, + &event->area, + flags); + } + cell_offset += column->displayed_width; + } - gtk_tree_view_motion_draw_column_motion_arrow (tree_view); + if (node == cursor && GTK_WIDGET_HAS_FOCUS (widget)) + gtk_tree_view_draw_focus (widget); - return TRUE; -} + if (node == drag_highlight) + { + /* Draw indicator for the drop + */ + gint highlight_y = -1; + GtkRBTree *tree = NULL; + GtkRBNode *node = NULL; + gint width; -static gboolean -gtk_tree_view_motion_bin_window (GtkWidget *widget, - GdkEventMotion *event) -{ - GtkTreeView *tree_view; - GtkRBTree *tree; - GtkRBNode *node; - gint new_y; - GtkRBTree *old_prelight_tree; - GtkRBNode *old_prelight_node; + switch (tree_view->priv->drag_dest_pos) + { + case GTK_TREE_VIEW_DROP_BEFORE: + highlight_y = background_area.y - vertical_separator/2; + break; - tree_view = (GtkTreeView *) widget; + case GTK_TREE_VIEW_DROP_AFTER: + highlight_y = background_area.y + background_area.height + vertical_separator/2; + break; - if (tree_view->priv->tree == NULL) - return FALSE; + case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: + case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: + _gtk_tree_view_find_node (tree_view, drag_dest_path, &tree, &node); - gtk_tree_view_maybe_begin_dragging_row (tree_view, event); + if (tree == NULL) + break; + gdk_drawable_get_size (tree_view->priv->bin_window, + &width, NULL); + gtk_paint_focus (widget->style, + tree_view->priv->bin_window, + NULL, + widget, + "add-mode", + 0, BACKGROUND_FIRST_PIXEL (tree_view, tree, node), + width - 1, BACKGROUND_HEIGHT (node) - 1); - old_prelight_tree = tree_view->priv->prelight_tree; - old_prelight_node = tree_view->priv->prelight_node; + break; + } - do_unprelight (tree_view, event->x, event->y); + if (highlight_y >= 0) + { + gdk_draw_line (event->window, + widget->style->black_gc, + highlight_x, + highlight_y, + bin_window_width - highlight_x, + highlight_y); + } + } - new_y = ((gint)event->yy; + y_offset += max_height; + if (node->children) + { + GtkTreeIter parent = iter; + gboolean has_child; - _gtk_rbtree_find_offset (tree_view->priv->tree, - TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, new_y), - &tree, - &node); + tree = node->children; + node = tree->root; - if (tree == NULL) - return TRUE; + g_assert (node != tree->nil); - /* If we are currently pressing down a button, we don't want to prelight anything else. */ - if ((tree_view->priv->button_pressed_node != NULL) && - (tree_view->priv->button_pressed_node != node)) - return TRUE; + while (node->left != tree->nil) + node = node->left; + has_child = gtk_tree_model_iter_children (tree_view->priv->model, + &iter, + &parent); + cell = gtk_tree_view_get_column (tree_view, 0)->cell; + depth++; + /* Sanity Check! */ + TREE_VIEW_INTERNAL_ASSERT (has_child, FALSE); + } + else + { + gboolean done = FALSE; + do + { + node = _gtk_rbtree_next (tree, node); + if (node != NULL) + { + gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter); + cell = gtk_tree_view_get_column (tree_view, 0)->cell; + done = TRUE; - do_prelight (tree_view, tree, node, event->x, new_y); + /* Sanity Check! */ + TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE); + } + else + { + GtkTreeIter parent_iter = iter; + gboolean has_parent; - if (old_prelight_node != tree_view->priv->prelight_node) - { - if (old_prelight_node) - gtk_tree_view_queue_draw_node (tree_view, - old_prelight_tree, - old_prelight_node, - NULL); + node = tree->parent_node; + tree = tree->parent_tree; + if (tree == NULL) + /* we've run out of tree. It's okay to return though, as + * we'd only break out of the while loop below. */ + return TRUE; + has_parent = gtk_tree_model_iter_parent (tree_view->priv->model, + &iter, + &parent_iter); + depth--; - if (tree_view->priv->prelight_node) - gtk_tree_view_queue_draw_node (tree_view, - tree_view->priv->prelight_tree, - tree_view->priv->prelight_node, - NULL); + /* Sanity check */ + TREE_VIEW_INTERNAL_ASSERT (has_parent, FALSE); + } + } + while (!done); + } } + while (y_offset < event->area.height); + + if (cursor_path) + gtk_tree_path_free (cursor_path); + + if (drag_dest_path) + gtk_tree_path_free (drag_dest_path); return TRUE; } static gboolean -gtk_tree_view_motion (GtkWidget *widget, - GdkEventMotion *event) +gtk_tree_view_expose (GtkWidget *widget, + GdkEventExpose *event) { GtkTreeView *tree_view; - tree_view = (GtkTreeView *) widget; - - if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE)) - return gtk_tree_view_motion_resize_column (widget, event); + g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE); - /* Drag column */ - if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_DRAG)) - return gtk_tree_view_motion_drag_column (widget, event); + tree_view = GTK_TREE_VIEW (widget); - /* Sanity check it */ if (event->window == tree_view->priv->bin_window) - return gtk_tree_view_motion_bin_window (widget, event); - return FALSE; + return gtk_tree_view_bin_expose (widget, event); + + return TRUE; } /* FIXME Is this function necessary? Can I get an enter_notify event @@ -2028,941 +2449,880 @@ gtk_tree_view_leave_notify (GtkWidget *widget, return TRUE; } -static gboolean -gtk_tree_view_button_press (GtkWidget *widget, - GdkEventButton *event) + +static gint +gtk_tree_view_focus_in (GtkWidget *widget, + GdkEventFocus *event) { GtkTreeView *tree_view; - GList *list; - GtkTreeViewColumn *column = NULL; - gint i; - GdkRectangle background_area; - GdkRectangle cell_area; - gint vertical_separator; g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE); tree_view = GTK_TREE_VIEW (widget); - gtk_widget_style_get (widget, "vertical_separator", &vertical_separator, NULL); - if (event->window == tree_view->priv->bin_window) - { - GtkRBNode *node; - GtkRBTree *tree; - GtkTreePath *path; - gchar *path_string; - gint depth; - gint new_y; - gint y_offset; - GtkTreeViewColumn *column = NULL; + GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS); - if (!GTK_WIDGET_HAS_FOCUS (widget)) - gtk_widget_grab_focus (widget); - GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS); + /* FIXME don't redraw so much */ + gtk_widget_queue_draw (widget); - /* are we in an arrow? */ - if (tree_view->priv->prelight_node && - GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT)) - { - if (event->button == 1) - { - gtk_grab_add (widget); - tree_view->priv->button_pressed_node = tree_view->priv->prelight_node; - tree_view->priv->button_pressed_tree = tree_view->priv->prelight_tree; - gtk_tree_view_draw_arrow (GTK_TREE_VIEW (widget), - tree_view->priv->prelight_tree, - tree_view->priv->prelight_node, - event->x, - event->y); - } - return TRUE; - } + return FALSE; +} + + +static gint +gtk_tree_view_focus_out (GtkWidget *widget, + GdkEventFocus *event) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS); + + /* FIXME don't redraw so much */ + gtk_widget_queue_draw (widget); - /* find the node that was clicked */ - new_y = ((gint)event->yy; - y_offset = -_gtk_rbtree_find_offset (tree_view->priv->tree, - TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, new_y), - &tree, - &node) + new_y - (gint)event->y; + return FALSE; +} - if (node == NULL) - /* We clicked in dead space */ - return TRUE; - /* Get the path and the node */ - path = _gtk_tree_view_find_path (tree_view, tree, node); - depth = gtk_tree_path_get_depth (path); - background_area.y = y_offset + event->y + vertical_separator; - background_area.height = GTK_RBNODE_GET_HEIGHT (node) - vertical_separator; - background_area.x = 0; - /* Let the cell have a chance at selecting it. */ +/* Drag-and-drop */ - for (i = 0, list = tree_view->priv->columns; i < tree_view->priv->n_columns; i++, list = list->next) - { - GtkCellRenderer *cell; - GtkTreeIter iter; - gboolean visible; - gboolean can_activate; +static void +set_source_row (GdkDragContext *context, + GtkTreeModel *model, + GtkTreePath *source_row) +{ + g_object_set_data_full (G_OBJECT (context), + "gtk-tree-view-source-row", + source_row ? gtk_tree_row_reference_new (model, source_row) : NULL, + (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL)); +} - column = list->data; +static GtkTreePath* +get_source_row (GdkDragContext *context) +{ + GtkTreeRowReference *ref = + g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row"); - if (!column->visible) - continue; + if (ref) + return gtk_tree_row_reference_get_path (ref); + else + return NULL; +} - background_area.width = column->displayed_width; - if (i == tree_view->priv->expander_column && - TREE_VIEW_DRAW_EXPANDERS(tree_view)) - { - cell_area = background_area; - cell_area.x += depth*tree_view->priv->tab_offset; - cell_area.width -= depth*tree_view->priv->tab_offset; - } - else - { - cell_area = background_area; - } - cell = column->cell; +static void +set_dest_row (GdkDragContext *context, + GtkTreeModel *model, + GtkTreePath *dest_row) +{ + g_object_set_data_full (G_OBJECT (context), + "gtk-tree-view-dest-row", + dest_row ? gtk_tree_row_reference_new (model, dest_row) : NULL, + (GDestroyNotify) (dest_row ? gtk_tree_row_reference_free : NULL)); +} - if ((background_area.x > (gint) event->x) || - (background_area.y > (gint) event->y) || - (background_area.x + background_area.width <= (gint) event->x) || - (background_area.y + background_area.height <= (gint) event->y)) - { - background_area.x += background_area.width; - continue; - } +static GtkTreePath* +get_dest_row (GdkDragContext *context) +{ + GtkTreeRowReference *ref = + g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row"); - gtk_tree_model_get_iter (tree_view->priv->model, - &iter, - path); - gtk_tree_view_column_set_cell_data (column, - tree_view->priv->model, - &iter); + if (ref) + return gtk_tree_row_reference_get_path (ref); + else + return NULL; +} - path_string = gtk_tree_path_to_string (path); +/* Get/set whether drag_motion requested the drag data and + * drag_data_received should thus not actually insert the data, + * since the data doesn't result from a drop. + */ +static void +set_status_pending (GdkDragContext *context, + GdkDragAction suggested_action) +{ + g_object_set_data (G_OBJECT (context), + "gtk-tree-view-status-pending", + GINT_TO_POINTER (suggested_action)); +} - g_object_get (G_OBJECT (cell), - "visible", &visible, - "can_activate", &can_activate, - NULL); - if (visible && - can_activate && - gtk_cell_renderer_event (cell, (GdkEvent *)event, - widget, path_string, - &background_area, - &cell_area, 0)) - { - g_free (path_string); - gtk_tree_path_free (path); - return TRUE; - } - else - { - g_free (path_string); - break; - } - } +static GdkDragAction +get_status_pending (GdkDragContext *context) +{ + return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context), + "gtk-tree-view-status-pending")); +} - if (column == NULL) - return FALSE; +static TreeViewDragInfo* +get_info (GtkTreeView *tree_view) +{ + return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info"); +} - /* Save press to possibly begin a drag - */ - if (tree_view->priv->pressed_button < 0) - { - tree_view->priv->pressed_button = event->button; - tree_view->priv->press_start_x = event->x; - tree_view->priv->press_start_y = event->y; - } +static void +clear_source_info (TreeViewDragInfo *di) +{ + if (di->source_target_list) + gtk_target_list_unref (di->source_target_list); - /* Handle the selection */ - if (tree_view->priv->selection == NULL) - tree_view->priv->selection = - _gtk_tree_selection_new_with_tree_view (tree_view); + if (di->row_draggable_closure) + g_closure_unref (di->row_draggable_closure); - _gtk_tree_selection_internal_select_node (tree_view->priv->selection, - node, - tree, - path, - event->state); + di->source_target_list = NULL; + di->row_draggable_closure = NULL; +} - if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) - gtk_tree_view_row_activated (tree_view, path, column); +static void +clear_dest_info (TreeViewDragInfo *di) +{ + if (di->location_droppable_closure) + g_closure_unref (di->location_droppable_closure); - gtk_tree_path_free (path); - return TRUE; - } + if (di->dest_target_list) + gtk_target_list_unref (di->dest_target_list); - for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++) - { - column = list->data; - if (event->window == column->window && - column->column_type == GTK_TREE_VIEW_COLUMN_RESIZEABLE && - column->window) - { - gpointer drag_data; + di->location_droppable_closure = NULL; + di->dest_target_list = NULL; +} - if (gdk_pointer_grab (column->window, FALSE, - GDK_POINTER_MOTION_HINT_MASK | - GDK_BUTTON1_MOTION_MASK | - GDK_BUTTON_RELEASE_MASK, - NULL, NULL, event->time)) - return FALSE; +static void +destroy_info (TreeViewDragInfo *di) +{ + clear_source_info (di); + clear_dest_info (di); + g_free (di); +} - gtk_grab_add (widget); - GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE); +static TreeViewDragInfo* +ensure_info (GtkTreeView *tree_view) +{ + TreeViewDragInfo *di; - /* block attached dnd signal handler */ - drag_data = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data"); - if (drag_data) - gtk_signal_handler_block_by_data (GTK_OBJECT (widget), drag_data); + di = get_info (tree_view); - if (!GTK_WIDGET_HAS_FOCUS (widget)) - gtk_widget_grab_focus (widget); + if (di == NULL) + { + di = g_new0 (TreeViewDragInfo, 1); - tree_view->priv->drag_pos = i; - tree_view->priv->x_drag = (column->button->allocation.x + column->button->allocation.width); - } + g_object_set_data_full (G_OBJECT (tree_view), + "gtk-tree-view-drag-info", + di, + (GDestroyNotify) destroy_info); } - return TRUE; + + return di; } -static gboolean -gtk_tree_view_button_release_drag_column (GtkWidget *widget, - GdkEventButton *event) +static void +remove_info (GtkTreeView *tree_view) +{ + g_object_set_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info", NULL); +} + +static gint +drag_scan_timeout (gpointer data) { GtkTreeView *tree_view; - GtkAllocation allocation; + gint x, y; + GdkModifierType state; + GtkTreePath *path = NULL; + GtkTreeViewColumn *column = NULL; + GdkRectangle visible_rect; - tree_view = GTK_TREE_VIEW (widget); + tree_view = GTK_TREE_VIEW (data); + + gdk_window_get_pointer (tree_view->priv->bin_window, + &x, &y, &state); + + gtk_tree_view_get_visible_rect (tree_view, &visible_rect); - allocation = tree_view->priv->drag_column->button->allocation; - allocation.x = tree_view->priv->drag_column_x; - gdk_pointer_ungrab (GDK_CURRENT_TIME); - gdk_window_reparent (tree_view->priv->drag_column->button->window, - tree_view->priv->header_window, - tree_view->priv->drag_column_x, - tree_view->priv->drag_column->button->allocation.y); - gtk_widget_set_parent_window (tree_view->priv->drag_column->button, tree_view->priv->header_window); - gtk_widget_size_allocate (tree_view->priv->drag_column->button, &allocation); + /* See if we are near the edge. */ + if ((x - visible_rect.x) < SCROLL_EDGE_SIZE || + (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE || + (y - visible_rect.y) < SCROLL_EDGE_SIZE || + (visible_rect.y + visible_rect.height - y) < SCROLL_EDGE_SIZE) + { + gtk_tree_view_get_path_at_pos (tree_view, + tree_view->priv->bin_window, + x, y, + &path, + &column, + NULL, + NULL); - tree_view->priv->drag_column = NULL; - gdk_window_hide (tree_view->priv->drag_window); + if (path != NULL) + { + gtk_tree_view_scroll_to_cell (tree_view, + path, + column, + 0.5, 0.5); - g_list_foreach (tree_view->priv->column_drag_info, (GFunc) g_free, NULL); - g_list_free (tree_view->priv->column_drag_info); - tree_view->priv->column_drag_info = NULL; - - gdk_window_hide (tree_view->priv->drag_highlight_window); + gtk_tree_path_free (path); + } + } - /* deal with headers */ - gdk_window_reparent (tree_view->priv->header_window, - widget->window, - 0, 0); - gdk_window_hide (tree_view->priv->drag_header_window); - return TRUE; } +static void +ensure_scroll_timeout (GtkTreeView *tree_view) +{ + if (tree_view->priv->scroll_timeout == 0) + tree_view->priv->scroll_timeout = gtk_timeout_add (50, drag_scan_timeout, tree_view); +} + +static void +remove_scroll_timeout (GtkTreeView *tree_view) +{ + if (tree_view->priv->scroll_timeout != 0) + { + gtk_timeout_remove (tree_view->priv->scroll_timeout); + tree_view->priv->scroll_timeout = 0; + } +} static gboolean -gtk_tree_view_button_release (GtkWidget *widget, - GdkEventButton *event) +check_model_dnd (GtkTreeModel *model, + GType required_iface, + const gchar *signal) { - GtkTreeView *tree_view; + if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface)) + { + g_warning ("You must override the default '%s' handler " + "on GtkTreeView when using models that don't support " + "the %s interface and enabling drag-and-drop. The simplest way to do this " + "is to connect to '%s' and call " + "gtk_signal_emit_stop_by_name() in your signal handler to prevent " + "the default handler from running. Look at the source code " + "for the default handler in gtktreeview.c to get an idea what " + "your handler should do. (gtktreeview.c is in the GTK source " + "code.) If you're using GTK from a language other than C, " + "there may be a more natural way to override default handlers, e.g. via derivation.", + signal, g_type_name (required_iface), signal); + return FALSE; + } + else + return TRUE; +} - g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE); - g_return_val_if_fail (event != NULL, FALSE); +static void +remove_open_timeout (GtkTreeView *tree_view) +{ + if (tree_view->priv->open_dest_timeout != 0) + { + gtk_timeout_remove (tree_view->priv->open_dest_timeout); + tree_view->priv->open_dest_timeout = 0; + } +} - tree_view = GTK_TREE_VIEW (widget); - if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_DRAG)) - return gtk_tree_view_button_release_drag_column (widget, event); +static gint +open_row_timeout (gpointer data) +{ + GtkTreeView *tree_view = data; + GtkTreePath *dest_path = NULL; + GtkTreeViewDropPosition pos; - if (tree_view->priv->pressed_button == event->button) - tree_view->priv->pressed_button = -1; + gtk_tree_view_get_drag_dest_row (tree_view, + &dest_path, + &pos); - if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE)) + if (dest_path && + (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER || + pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) { - gpointer drag_data; - gint width; - gint x; - gint i; - - i = tree_view->priv->drag_pos; - tree_view->priv->drag_pos = -1; - - /* unblock attached dnd signal handler */ - drag_data = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data"); - if (drag_data) - gtk_signal_handler_unblock_by_data (GTK_OBJECT (widget), drag_data); + gtk_tree_view_expand_row (tree_view, dest_path, FALSE); + tree_view->priv->open_dest_timeout = 0; - GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IN_COLUMN_RESIZE); - gtk_widget_get_pointer (widget, &x, NULL); - gtk_grab_remove (widget); - gdk_pointer_ungrab (event->time); + gtk_tree_path_free (dest_path); - width = gtk_tree_view_new_column_width (GTK_TREE_VIEW (widget), i, &x); - gtk_tree_view_column_set_width (gtk_tree_view_get_column (GTK_TREE_VIEW (widget), i), width); return FALSE; } + else + { + if (dest_path) + gtk_tree_path_free (dest_path); + return TRUE; + } +} - if (tree_view->priv->button_pressed_node == NULL) - return FALSE; +/* Returns TRUE if event should not be propagated to parent widgets */ +static gboolean +set_destination_row (GtkTreeView *tree_view, + GdkDragContext *context, + gint x, + gint y, + GdkDragAction *suggested_action, + GdkAtom *target) +{ + GtkTreePath *path = NULL; + GtkTreeViewDropPosition pos; + GtkTreeViewDropPosition old_pos; + TreeViewDragInfo *di; + GtkWidget *widget; + GtkTreePath *old_dest_path = NULL; - if (event->button == 1) - { - gtk_grab_remove (widget); - if (tree_view->priv->button_pressed_node == tree_view->priv->prelight_node && - GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT)) - { - GtkTreePath *path = NULL; - GtkTreeIter iter; + *suggested_action = 0; + *target = GDK_NONE; - /* Actually activate the node */ - if (tree_view->priv->button_pressed_node->children == NULL) - { - GtkTreeIter child; - path = _gtk_tree_view_find_path (tree_view, - tree_view->priv->button_pressed_tree, - tree_view->priv->button_pressed_node); - gtk_tree_model_get_iter (tree_view->priv->model, &iter, path); + widget = GTK_WIDGET (tree_view); - if (gtk_tree_model_iter_children (tree_view->priv->model, &child, &iter)) - { - gboolean expand; - g_signal_emit (G_OBJECT (tree_view), tree_view_signals[EXPAND_ROW], 0, &iter, path, &expand); - if (! expand) - { - tree_view->priv->button_pressed_node->children = _gtk_rbtree_new (); - tree_view->priv->button_pressed_node->children->parent_tree = tree_view->priv->button_pressed_tree; - tree_view->priv->button_pressed_node->children->parent_node = tree_view->priv->button_pressed_node; - gtk_tree_view_build_tree (tree_view, - tree_view->priv->button_pressed_node->children, - &child, - gtk_tree_path_get_depth (path) + 1, - FALSE, - GTK_WIDGET_REALIZED (widget)); - } - } - } - else - { - gboolean collapse; + di = get_info (tree_view); - path = _gtk_tree_view_find_path (GTK_TREE_VIEW (widget), - tree_view->priv->button_pressed_tree, - tree_view->priv->button_pressed_node); - gtk_tree_model_get_iter (tree_view->priv->model, - &iter, - path); - g_signal_emit (G_OBJECT (tree_view), tree_view_signals[COLLAPSE_ROW], 0, &iter, path, &collapse); + if (di == NULL) + { + /* someone unset us as a drag dest, note that if + * we return FALSE drag_leave isn't called + */ - if (! collapse) - { - GtkTreeIter child_iter; - gtk_tree_path_append_index (path, 0); - gtk_tree_model_iter_children (tree_view->priv->model, - &child_iter, - &iter); - gtk_tree_view_discover_dirty (GTK_TREE_VIEW (widget), - tree_view->priv->button_pressed_node->children, - &child_iter, - gtk_tree_path_get_depth (path)); - gtk_tree_view_unref_tree (GTK_TREE_VIEW (widget), - tree_view->priv->button_pressed_node->children); - _gtk_rbtree_remove (tree_view->priv->button_pressed_node->children); - } - } - if (path) - gtk_tree_path_free (path); + gtk_tree_view_set_drag_dest_row (tree_view, + NULL, + GTK_TREE_VIEW_DROP_BEFORE); - _gtk_tree_view_update_size (GTK_TREE_VIEW (widget)); - } + remove_scroll_timeout (GTK_TREE_VIEW (widget)); + remove_open_timeout (GTK_TREE_VIEW (widget)); - tree_view->priv->button_pressed_node = NULL; + return FALSE; /* no longer a drop site */ } - return TRUE; -} - -static void -gtk_tree_view_set_focus_child (GtkContainer *container, - GtkWidget *child) -{ - GtkTreeView *tree_view = GTK_TREE_VIEW (container); - GList *list; + *target = gtk_drag_dest_find_target (widget, context, di->dest_target_list); + if (*target == GDK_NONE) + { + return FALSE; + } - for (list = tree_view->priv->columns; list; list = list->next) + if (!gtk_tree_view_get_dest_row_at_pos (tree_view, + x, y, + &path, + &pos)) { - if (GTK_TREE_VIEW_COLUMN (list->data)->button == child) - { - tree_view->priv->focus_column = GTK_TREE_VIEW_COLUMN (list->data); - break; - } + /* can't drop here */ + remove_open_timeout (tree_view); + + gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), + NULL, + GTK_TREE_VIEW_DROP_BEFORE); + + /* don't propagate to parent though */ + return TRUE; } - (* parent_class->set_focus_child) (container, child); -} -static void -gtk_tree_view_draw_focus (GtkWidget *widget) -{ - GtkTreeView *tree_view; - GtkTreePath *cursor_path; - GtkRBTree *tree = NULL; - GtkRBNode *node = NULL; - gint x, y; - gint width, height; - gint vertical_separator; + g_assert (path); - g_return_if_fail (GTK_IS_TREE_VIEW (widget)); + /* If we left the current row's "open" zone, unset the timeout for + * opening the row + */ + gtk_tree_view_get_drag_dest_row (tree_view, + &old_dest_path, + &old_pos); - tree_view = GTK_TREE_VIEW (widget); - - gtk_widget_style_get (widget, "vertical_separator", &vertical_separator, NULL); + if (old_dest_path && + (gtk_tree_path_compare (path, old_dest_path) != 0 || + !(pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER || + pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE))) + remove_open_timeout (tree_view); - if (! GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS)) - return; + if (old_dest_path) + gtk_tree_path_free (old_dest_path); - if (tree_view->priv->cursor == NULL) - return; + if (TRUE /* FIXME if the location droppable predicate */) + { + GtkWidget *source_widget; - cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); - if (cursor_path == NULL) - return; + *suggested_action = context->suggested_action; - _gtk_tree_view_find_node (tree_view, cursor_path, &tree, &node); + source_widget = gtk_drag_get_source_widget (context); - if (tree == NULL) + if (source_widget == widget) + { + /* Default to MOVE, unless the user has + * pressed ctrl or alt to affect available actions + */ + if ((context->actions & GDK_ACTION_MOVE) != 0) + *suggested_action = GDK_ACTION_MOVE; + } + + gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), + path, pos); + } + else { - gtk_tree_path_free (cursor_path); - return; + /* can't drop here */ + remove_open_timeout (tree_view); + + gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), + NULL, + GTK_TREE_VIEW_DROP_BEFORE); } - gdk_drawable_get_size (tree_view->priv->bin_window, - &width, NULL); + return TRUE; +} +static GtkTreePath* +get_logical_dest_row (GtkTreeView *tree_view) +{ + /* adjust path to point to the row the drop goes in front of */ + GtkTreePath *path = NULL; + GtkTreeViewDropPosition pos; - x = 0; - y = BACKGROUND_FIRST_PIXEL (tree_view, tree, node); - gdk_drawable_get_size (tree_view->priv->bin_window, - &width, NULL); - width = width - 1; - height = BACKGROUND_HEIGHT (node) - 1; - if (tree_view->priv->focus_column != NULL) - { - gboolean visible; - gboolean can_focus; + gtk_tree_view_get_drag_dest_row (tree_view, &path, &pos); - g_object_get (G_OBJECT (tree_view->priv->focus_column->cell), - "can_activate", &can_focus, - "visible", &visible, - NULL); - if (can_focus && visible) - { - GtkTreeIter iter; - GdkRectangle cell_area; - gint x_offset; - gint y_offset; + if (path == NULL) + return NULL; - cell_area.x = tree_view->priv->focus_column->button->allocation.x; - cell_area.y = y; - cell_area.width = tree_view->priv->focus_column->displayed_width; - cell_area.height = CELL_HEIGHT (node, vertical_separator); + if (pos == GTK_TREE_VIEW_DROP_BEFORE) + ; /* do nothing */ + else if (pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE || + pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER) + { + /* get first child, drop before it */ + gtk_tree_path_append_index (path, 0); + } + else + { + g_assert (pos == GTK_TREE_VIEW_DROP_AFTER); + gtk_tree_path_next (path); + } - gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path); - gtk_tree_view_column_set_cell_data (tree_view->priv->focus_column, tree_view->priv->model, &iter); + return path; +} - gtk_cell_renderer_get_size (tree_view->priv->focus_column->cell, GTK_WIDGET (tree_view), &cell_area, &x_offset, &y_offset, &width, &height); - width += 2; - height += 2; - x = cell_area.x + x_offset - 1; - y = cell_area.y + y_offset - 1 + vertical_separator/2; - } - } +static gboolean +gtk_tree_view_maybe_begin_dragging_row (GtkTreeView *tree_view, + GdkEventMotion *event) +{ + GdkDragContext *context; + TreeViewDragInfo *di; + GtkTreePath *path = NULL; + gint button; + gint cell_x, cell_y; + GtkTreeModel *model; - gtk_paint_focus (widget->style, - tree_view->priv->bin_window, - NULL, - widget, - "add-mode", - x, y, width, height); + di = get_info (tree_view); - gtk_tree_path_free (cursor_path); -} + if (di == NULL) + return FALSE; + if (tree_view->priv->pressed_button < 0) + return FALSE; -static gint -gtk_tree_view_focus_in (GtkWidget *widget, - GdkEventFocus *event) -{ - GtkTreeView *tree_view; + if (!gtk_drag_check_threshold (GTK_WIDGET (tree_view), + tree_view->priv->press_start_x, + tree_view->priv->press_start_y, + event->x, event->y)) + return FALSE; - g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE); - g_return_val_if_fail (event != NULL, FALSE); + model = gtk_tree_view_get_model (tree_view); - tree_view = GTK_TREE_VIEW (widget); + if (model == NULL) + return FALSE; - GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS); + button = tree_view->priv->pressed_button; + tree_view->priv->pressed_button = -1; - /* FIXME don't redraw so much */ - gtk_widget_queue_draw (widget); + gtk_tree_view_get_path_at_pos (tree_view, + tree_view->priv->bin_window, + tree_view->priv->press_start_x, + tree_view->priv->press_start_y, + &path, + NULL, + &cell_x, + &cell_y); - return FALSE; -} + if (path == NULL) + return FALSE; + /* FIXME if the path doesn't match the row_draggable predicate, + * return FALSE and free path + */ -static gint -gtk_tree_view_focus_out (GtkWidget *widget, - GdkEventFocus *event) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE); - g_return_val_if_fail (event != NULL, FALSE); + /* FIXME Check whether we're a start button, if not return FALSE and + * free path + */ - GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS); + context = gtk_drag_begin (GTK_WIDGET (tree_view), + di->source_target_list, + di->source_actions, + button, + (GdkEvent*)event); - /* FIXME don't redraw so much */ - gtk_widget_queue_draw (widget); + gtk_drag_set_icon_default (context); - return FALSE; -} + { + GdkPixmap *row_pix; -/* Returns TRUE if the focus is within the headers, after the focus operation is - * done - */ -static gboolean -gtk_tree_view_header_focus (GtkTreeView *tree_view, - GtkDirectionType dir) -{ - GtkWidget *focus_child; - GtkContainer *container; + row_pix = gtk_tree_view_create_row_drag_icon (tree_view, + path); - GList *last_column, *first_column; - GList *tmp_list; + gtk_drag_set_icon_pixmap (context, + gdk_drawable_get_colormap (row_pix), + row_pix, + NULL, + /* the + 1 is for the black border in the icon */ + tree_view->priv->press_start_x + 1, + cell_y + 1); - if (! GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE)) - return FALSE; + gdk_pixmap_unref (row_pix); + } - focus_child = GTK_CONTAINER (tree_view)->focus_child; - container = GTK_CONTAINER (tree_view); + set_source_row (context, model, path); + gtk_tree_path_free (path); - for (last_column = g_list_last (tree_view->priv->columns); - last_column && - !(GTK_TREE_VIEW_COLUMN (last_column->data)->visible) && - GTK_WIDGET_CAN_FOCUS (GTK_TREE_VIEW_COLUMN (last_column->data)->button); - last_column = last_column->prev); + return TRUE; +} - for (first_column = tree_view->priv->columns; - first_column && - !(GTK_TREE_VIEW_COLUMN (first_column->data)->visible) && - GTK_WIDGET_CAN_FOCUS (GTK_TREE_VIEW_COLUMN (first_column->data)->button); - first_column = first_column->next); - /* No headers are visible, or are focusable. We can't focus in or out. - */ - if (last_column == NULL) - return FALSE; +static void +gtk_tree_view_drag_begin (GtkWidget *widget, + GdkDragContext *context) +{ + /* do nothing */ +} - switch (dir) - { - case GTK_DIR_TAB_BACKWARD: - case GTK_DIR_TAB_FORWARD: - case GTK_DIR_UP: - case GTK_DIR_DOWN: - if (focus_child == NULL) - { - if (tree_view->priv->focus_column != NULL) - focus_child = tree_view->priv->focus_column->button; - else - focus_child = GTK_TREE_VIEW_COLUMN (first_column->data)->button; - gtk_widget_grab_focus (focus_child); - break; - } - return FALSE; +static void +gtk_tree_view_drag_end (GtkWidget *widget, + GdkDragContext *context) +{ + /* do nothing */ +} - case GTK_DIR_LEFT: - case GTK_DIR_RIGHT: - if (focus_child == NULL) - { - if (tree_view->priv->focus_column != NULL) - focus_child = tree_view->priv->focus_column->button; - else if (dir == GTK_DIR_LEFT) - focus_child = GTK_TREE_VIEW_COLUMN (last_column->data)->button; - else - focus_child = GTK_TREE_VIEW_COLUMN (first_column->data)->button; - gtk_widget_grab_focus (focus_child); - break; - } +/* Default signal implementations for the drag signals */ +static void +gtk_tree_view_drag_data_get (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint time) +{ + GtkTreeView *tree_view; + GtkTreeModel *model; + TreeViewDragInfo *di; + GtkTreePath *source_row; - if (gtk_container_focus (GTK_CONTAINER (focus_child), dir)) - { - /* The focus moves inside the button. */ - /* This is probably a great example of bad UI */ - break; - } + tree_view = GTK_TREE_VIEW (widget); - /* We need to move the focus among the row of buttons. */ - for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next) - if (GTK_TREE_VIEW_COLUMN (tmp_list->data)->button == focus_child) - break; + model = gtk_tree_view_get_model (tree_view); - if (tmp_list == first_column && dir == GTK_DIR_LEFT) - { - focus_child = GTK_TREE_VIEW_COLUMN (last_column->data)->button; - gtk_widget_grab_focus (focus_child); - break; - } - else if (tmp_list == last_column && dir == GTK_DIR_RIGHT) - { - focus_child = GTK_TREE_VIEW_COLUMN (first_column->data)->button; - gtk_widget_grab_focus (focus_child); - break; - } + if (model == NULL) + return; - while (tmp_list) - { - GtkTreeViewColumn *column; + di = get_info (GTK_TREE_VIEW (widget)); - if (dir == GTK_DIR_RIGHT) - tmp_list = tmp_list->next; - else - tmp_list = tmp_list->prev; + if (di == NULL) + return; - if (tmp_list == NULL) - { - g_warning ("Internal button not found"); - break; - } - column = tmp_list->data; - if (column->button && - column->visible && - GTK_WIDGET_CAN_FOCUS (column->button)) - { - focus_child = column->button; - gtk_widget_grab_focus (column->button); - break; - } - } - break; - default: - g_assert_not_reached (); - break; - } + source_row = get_source_row (context); - /* if focus child is non-null, we assume it's been set to the current focus child + if (source_row == NULL) + return; + + /* We can implement the GTK_TREE_MODEL_ROW target generically for + * any model; for DragSource models there are some other targets + * we also support. */ - if (focus_child) - { - for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next) - if (GTK_TREE_VIEW_COLUMN (tmp_list->data)->button == focus_child) - break; - tree_view->priv->focus_column = GTK_TREE_VIEW_COLUMN (tmp_list->data); + if (GTK_IS_TREE_DRAG_SOURCE (model) && + gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model), + source_row, + selection_data)) + goto done; - /* If the following isn't true, then the view is smaller then the scrollpane. - */ - if ((focus_child->allocation.x + focus_child->allocation.width) <= - (tree_view->priv->hadjustment->upper)) - { - /* Scroll to the button, if needed */ - if ((tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size) < - (focus_child->allocation.x + focus_child->allocation.width)) - gtk_adjustment_set_value (tree_view->priv->hadjustment, - focus_child->allocation.x + focus_child->allocation.width - - tree_view->priv->hadjustment->page_size); - else if (tree_view->priv->hadjustment->value > focus_child->allocation.x) - gtk_adjustment_set_value (tree_view->priv->hadjustment, - focus_child->allocation.x); - } + /* If drag_data_get does nothing, try providing row data. */ + if (selection_data->target == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE)) + { + gtk_selection_data_set_tree_row (selection_data, + model, + source_row); } - return (focus_child != NULL); + done: + gtk_tree_path_free (source_row); } -/* WARNING: Somewhat scary function */ -/* We make the assumption that if container->focus_child != NULL, the focus must - * be in the header. For now, this is accurate. It may not be in the future. - */ -/* The sordid relationship between focus_column and scroll_column: - * - * The focus_column represents the column that currently has keyboard focus, and - * is used when navigating columns by keyboard. scroll_column is used for - * handling scrolling by keyboard, such that in cases. - */ -static gint -gtk_tree_view_focus (GtkContainer *container, - GtkDirectionType direction) +static void +gtk_tree_view_drag_data_delete (GtkWidget *widget, + GdkDragContext *context) { + TreeViewDragInfo *di; + GtkTreeModel *model; GtkTreeView *tree_view; - GtkWidget *focus_child; - GdkEvent *event; - GtkRBTree *cursor_tree; - GtkRBNode *cursor_node; - GtkTreePath *cursor_path; + GtkTreePath *source_row; - g_return_val_if_fail (GTK_IS_TREE_VIEW (container), FALSE); - g_return_val_if_fail (GTK_WIDGET_VISIBLE (container), FALSE); + tree_view = GTK_TREE_VIEW (widget); + model = gtk_tree_view_get_model (tree_view); - tree_view = GTK_TREE_VIEW (container); + if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete")) + return; - if (!GTK_WIDGET_IS_SENSITIVE (container)) - return FALSE; + di = get_info (tree_view); - focus_child = container->focus_child; + if (di == NULL) + return; - /* Case 1. Headers have focus. */ - if (focus_child) - { - switch (direction) - { - case GTK_DIR_LEFT: - case GTK_DIR_RIGHT: - return (gtk_tree_view_header_focus (tree_view, direction)); - case GTK_DIR_TAB_BACKWARD: - case GTK_DIR_UP: - return FALSE; - case GTK_DIR_TAB_FORWARD: - case GTK_DIR_DOWN: + source_row = get_source_row (context); - if (tree_view->priv->tree == NULL) - return FALSE; + if (source_row == NULL) + return; - GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS); - gtk_widget_grab_focus (GTK_WIDGET (container)); + gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model), + source_row); - if (tree_view->priv->selection == NULL) - tree_view->priv->selection = - _gtk_tree_selection_new_with_tree_view (tree_view); + gtk_tree_path_free (source_row); - /* if there is no keyboard focus yet, we select the first node - */ + set_source_row (context, NULL, NULL); +} - cursor_path = NULL; +static void +gtk_tree_view_drag_leave (GtkWidget *widget, + GdkDragContext *context, + guint time) +{ + TreeViewDragInfo *di; - if (tree_view->priv->cursor) - cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); + di = get_info (GTK_TREE_VIEW (widget)); - if (cursor_path == NULL) - { - GtkTreePath *tmp_path = gtk_tree_path_new_root (); + /* unset any highlight row */ + gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), + NULL, + GTK_TREE_VIEW_DROP_BEFORE); - if (tree_view->priv->cursor) - gtk_tree_row_reference_free (tree_view->priv->cursor); + remove_scroll_timeout (GTK_TREE_VIEW (widget)); + remove_open_timeout (GTK_TREE_VIEW (widget)); +} - tree_view->priv->cursor = - gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, tmp_path); - cursor_path = tmp_path; - } - gtk_tree_selection_select_path (tree_view->priv->selection, - cursor_path); +static gboolean +gtk_tree_view_drag_motion (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time) +{ + GtkTreePath *path = NULL; + GtkTreeViewDropPosition pos; + GtkTreeView *tree_view; + GdkDragAction suggested_action = 0; + GdkAtom target; + + tree_view = GTK_TREE_VIEW (widget); + + if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target)) + return FALSE; - gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL); + ensure_scroll_timeout (tree_view); - gtk_tree_path_free (cursor_path); + gtk_tree_view_get_drag_dest_row (tree_view, &path, &pos); - return TRUE; - } + if (path == NULL) + { + /* Can't drop here. */ + gdk_drag_status (context, 0, time); } - - /* Case 2. We don't have focus at all. */ - if (!GTK_WIDGET_HAS_FOCUS (container)) + else { - if ((direction == GTK_DIR_TAB_FORWARD) || - (direction == GTK_DIR_RIGHT) || - (direction == GTK_DIR_DOWN) || - (direction == GTK_DIR_LEFT) || - (tree_view->priv->tree == NULL)) - return gtk_tree_view_header_focus (tree_view, direction); + if (tree_view->priv->open_dest_timeout == 0 && + (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER || + pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) + { + tree_view->priv->open_dest_timeout = + gtk_timeout_add (500, open_row_timeout, tree_view); + } - /* The headers didn't want the focus, so we take it. */ - GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS); - gtk_widget_grab_focus (GTK_WIDGET (container)); + if (target == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE)) + { + /* Request data so we can use the source row when + * determining whether to accept the drop + */ + set_status_pending (context, suggested_action); + gtk_drag_get_data (widget, context, target, time); + } + else + { + set_status_pending (context, 0); + gdk_drag_status (context, suggested_action, time); + } + } - if (tree_view->priv->selection == NULL) - tree_view->priv->selection = - _gtk_tree_selection_new_with_tree_view (tree_view); + if (path) + gtk_tree_path_free (path); - cursor_path = NULL; - if (tree_view->priv->cursor) - cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); + return TRUE; +} - if (cursor_path == NULL) - { - GtkTreePath *tmp_path = gtk_tree_path_new_root (); - if (tree_view->priv->cursor) - gtk_tree_row_reference_free (tree_view->priv->cursor); +static gboolean +gtk_tree_view_drag_drop (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time) +{ + GtkTreeView *tree_view; + GtkTreePath *path; + GdkDragAction suggested_action = 0; + GdkAtom target = GDK_NONE; + TreeViewDragInfo *di; + GtkTreeModel *model; - tree_view->priv->cursor = - gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, tmp_path); - cursor_path = tmp_path; - } + tree_view = GTK_TREE_VIEW (widget); - gtk_tree_selection_select_path (tree_view->priv->selection, - cursor_path); + model = gtk_tree_view_get_model (tree_view); - gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL); + remove_scroll_timeout (GTK_TREE_VIEW (widget)); + remove_open_timeout (GTK_TREE_VIEW (widget)); - gtk_tree_path_free (cursor_path); + di = get_info (tree_view); - return TRUE; - } + if (di == NULL) + return FALSE; - /* Case 3. We have focus already. */ - if (tree_view->priv->tree == NULL) + if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop")) return FALSE; - if (direction == GTK_DIR_TAB_BACKWARD) - return (gtk_tree_view_header_focus (tree_view, direction)); - else if (direction == GTK_DIR_TAB_FORWARD) + if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target)) return FALSE; - cursor_path = NULL; - if (tree_view->priv->cursor) - cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); + path = get_logical_dest_row (tree_view); - /* Do we have a focus path? We should, unless it was deleted. */ - /* in that case, we pick the first one arbitrarily */ - if (cursor_path == NULL) + if (target != GDK_NONE && path != NULL) { - GtkTreePath *tmp_path = gtk_tree_path_new_root (); - - if (tree_view->priv->cursor) - gtk_tree_row_reference_free (tree_view->priv->cursor); - - tree_view->priv->cursor = - gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, tmp_path); - cursor_path = tmp_path; - - gtk_tree_selection_select_path (tree_view->priv->selection, - cursor_path); - - gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL); - - gtk_tree_path_free (cursor_path); + /* in case a motion had requested drag data, change things so we + * treat drag data receives as a drop. + */ + set_status_pending (context, 0); - return TRUE; + set_dest_row (context, model, path); } - /* Case 4. We have focus already. Move the cursor. */ - if (direction == GTK_DIR_LEFT) - { - gdouble val; - val = tree_view->priv->hadjustment->value - tree_view->priv->hadjustment->page_size/2; - val = MAX (val, 0.0); - gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->hadjustment), val); - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); + if (path) + gtk_tree_path_free (path); - gtk_tree_path_free (cursor_path); + /* Unset this thing */ + gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), + NULL, + GTK_TREE_VIEW_DROP_BEFORE); - return TRUE; - } - if (direction == GTK_DIR_RIGHT) + if (target != GDK_NONE) { - gdouble val; - val = tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size/2; - val = MIN (tree_view->priv->hadjustment->upper - tree_view->priv->hadjustment->page_size, val); - gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->hadjustment), val); - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - - gtk_tree_path_free (cursor_path); - + gtk_drag_get_data (widget, context, target, time); return TRUE; } + else + return FALSE; +} - cursor_tree = NULL; - cursor_node = NULL; +static void +gtk_tree_view_drag_data_received (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint time) +{ + GtkTreePath *path; + TreeViewDragInfo *di; + gboolean accepted = FALSE; + GtkTreeModel *model; + GtkTreeView *tree_view; + GtkTreePath *dest_row; + GdkDragAction suggested_action; - _gtk_tree_view_find_node (tree_view, cursor_path, - &cursor_tree, - &cursor_node); + tree_view = GTK_TREE_VIEW (widget); - /* undraw the old row */ - gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL); + model = gtk_tree_view_get_model (tree_view); - gtk_tree_path_free (cursor_path); - cursor_path = NULL; + if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received")) + return; - if (tree_view->priv->cursor) - { - gtk_tree_row_reference_free (tree_view->priv->cursor); - tree_view->priv->cursor = NULL; - } + di = get_info (tree_view); - switch (direction) - { - case GTK_DIR_TAB_BACKWARD: - case GTK_DIR_UP: - _gtk_rbtree_prev_full (cursor_tree, - cursor_node, - &cursor_tree, - &cursor_node); - break; - case GTK_DIR_TAB_FORWARD: - case GTK_DIR_DOWN: - _gtk_rbtree_next_full (cursor_tree, - cursor_node, - &cursor_tree, - &cursor_node); - break; - default: - break; - } + if (di == NULL) + return; - if (cursor_node) + suggested_action = get_status_pending (context); + + if (suggested_action) { - GdkModifierType state = 0; + /* We are getting this data due to a request in drag_motion, + * rather than due to a request in drag_drop, so we are just + * supposed to call drag_status, not actually paste in the + * data. + */ + path = get_logical_dest_row (tree_view); - event = gtk_get_current_event (); - if (event) - gdk_event_get_state (event, &state); + if (path == NULL) + suggested_action = 0; - if (event) - gdk_event_free (event); + if (suggested_action) + { + GtkTreeModel *src_model = NULL; + GtkTreePath *src_path = NULL; - cursor_path = _gtk_tree_view_find_path (tree_view, - cursor_tree, - cursor_node); + if (!gtk_selection_data_get_tree_row (selection_data, + &src_model, + &src_path)) + suggested_action = 0; - if (cursor_path) - { - _gtk_tree_selection_internal_select_node (tree_view->priv->selection, - cursor_node, - cursor_tree, - cursor_path, - state); + if (suggested_action) + { + if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model), + src_model, + src_path, + path)) + suggested_action = 0; - tree_view->priv->cursor = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, cursor_path); + gtk_tree_path_free (src_path); + } + } + gdk_drag_status (context, suggested_action, time); - /* draw the newly-selected row */ - gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL); + if (path) + gtk_tree_path_free (path); - gtk_tree_path_free (cursor_path); - } + /* If you can't drop, remove user drop indicator until the next motion */ + if (suggested_action == 0) + gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), + NULL, + GTK_TREE_VIEW_DROP_BEFORE); - gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node); - gtk_widget_grab_focus (GTK_WIDGET (tree_view)); + return; + } - return TRUE; + dest_row = get_dest_row (context); + + if (dest_row == NULL) + return; + + if (selection_data->length >= 0) + { + if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model), + dest_row, + selection_data)) + accepted = TRUE; } - /* At this point, we've progressed beyond the edge of the rows. */ + gtk_drag_finish (context, + accepted, + (context->action == GDK_ACTION_MOVE), + time); - if (direction == GTK_DIR_UP) - /* We can't go back anymore. Try the headers */ - return (gtk_tree_view_header_focus (tree_view, direction)); + gtk_tree_path_free (dest_row); - /* we've reached the end of the tree. Go on. */ - return FALSE; + /* drop dest_row */ + set_dest_row (context, NULL, NULL); } -/* Container method + + +/* GtkContainer Methods */ + + static void gtk_tree_view_remove (GtkContainer *container, GtkWidget *widget) @@ -3044,1348 +3404,1022 @@ gtk_tree_view_forall (GtkContainer *container, } } -/** - * gtk_tree_view_row_activated: - * @tree_view: A #GtkTreeView - * @path: The #GtkTreePath to be activated. - * @column: The #GtkTreeViewColumn to be activated. - * - * Activates the cell determined by @path and @column. - **/ -void -gtk_tree_view_row_activated (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column) -{ - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - /* FIXME: Actually activate the path internally, not just emit the signal */ - g_signal_emit (G_OBJECT(tree_view), tree_view_signals[ROW_ACTIVATED], 0, path, column); -} - - -/** - * gtk_tree_view_map_expanded_rows: - * @tree_view: A #GtkTreeView - * @func: A function to be called - * @data: User data to be passed to the function. - * - * Calls @func on all expanded rows. - **/ -void -gtk_tree_view_map_expanded_rows (GtkTreeView *tree_view, - GtkTreeViewMappingFunc func, - gpointer data) -{ - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (func != NULL); - - -} - -/* TreeModel Callbacks +/* Returns TRUE if the focus is within the headers, after the focus operation is + * done */ - -static void -gtk_tree_view_range_changed (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - GtkTreePath *end_path, - GtkTreeIter *end_iter, - gpointer data) -{ - GtkTreeView *tree_view = (GtkTreeView *)data; - GtkRBTree *tree; - GtkRBNode *node; - gint height; - gboolean dirty_marked; - gboolean free_path = FALSE; - gint vertical_separator; - - g_return_if_fail (path != NULL || iter != NULL); - - gtk_widget_style_get (GTK_WIDGET (data), "vertical_separator", &vertical_separator, NULL); - - if (path == NULL) - { - path = gtk_tree_model_get_path (model, iter); - free_path = TRUE; - } - else if (iter == NULL) - gtk_tree_model_get_iter (model, iter, path); - - if (_gtk_tree_view_find_node (tree_view, - path, - &tree, - &node)) - /* We aren't actually showing the node */ - goto done; - - if (tree == NULL) - goto done; - - dirty_marked = gtk_tree_view_discover_dirty_iter (tree_view, - iter, - gtk_tree_path_get_depth (path), - &height); - - if (GTK_RBNODE_GET_HEIGHT (node) != height + vertical_separator) - { - _gtk_rbtree_node_set_height (tree, node, height + vertical_separator); - gtk_widget_queue_resize (GTK_WIDGET (data)); - goto done; - } - if (dirty_marked) - gtk_widget_queue_resize (GTK_WIDGET (data)); - else - { - gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL); - } - - done: - if (free_path) - gtk_tree_path_free (path); -} - -static void -gtk_tree_view_inserted (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - GtkTreeView *tree_view = (GtkTreeView *) data; - gint *indices; - GtkRBTree *tmptree, *tree; - GtkRBNode *tmpnode = NULL; - gint max_height; - gint depth; - gint i = 0; - gboolean free_path = FALSE; - - if (tree_view->priv->tree == NULL) - tree_view->priv->tree = _gtk_rbtree_new (); - - tmptree = tree = tree_view->priv->tree; - g_return_if_fail (path != NULL || iter != NULL); - - if (path == NULL) - { - path = gtk_tree_model_get_path (model, iter); - free_path = TRUE; - } - else if (iter == NULL) - gtk_tree_model_get_iter (model, iter, path); - - /* Update all row-references */ - gtk_tree_row_reference_inserted (G_OBJECT (data), path); - - depth = gtk_tree_path_get_depth (path); - indices = gtk_tree_path_get_indices (path); - - /* First, find the parent tree */ - while (i < depth - 1) - { - if (tmptree == NULL) - { - /* We aren't showing the node */ - goto done; - } - - tmpnode = _gtk_rbtree_find_count (tmptree, indices[i] + 1); - if (tmpnode == NULL) - { - g_warning ("A node was inserted with a parent that's not in the tree.\n" \ - "This possibly means that a GtkTreeModel inserted a child node\n" \ - "before the parent was inserted."); - goto done; - } - else if (!GTK_RBNODE_FLAG_SET (tmpnode, GTK_RBNODE_IS_PARENT)) - { - /* FIXME enforce correct behavior on model, probably */ - /* In theory, the model should have emitted has_child_toggled here. We - * try to catch it anyway, just to be safe, in case the model hasn't. - */ - GtkTreePath *tmppath = _gtk_tree_view_find_path (tree_view, - tree, - tmpnode); - gtk_tree_view_has_child_toggled (model, tmppath, NULL, data); - gtk_tree_path_free (tmppath); - goto done; - } - - tmptree = tmpnode->children; - tree = tmptree; - i++; - } - - if (tree == NULL) - goto done; - - /* ref the node */ - gtk_tree_model_ref_node (tree_view->priv->model, iter); - max_height = gtk_tree_view_insert_iter_height (tree_view, - tree, - iter, - depth); - if (indices[depth - 1] == 0) - { - tmpnode = _gtk_rbtree_find_count (tree, 1); - _gtk_rbtree_insert_before (tree, tmpnode, max_height); - } - else - { - tmpnode = _gtk_rbtree_find_count (tree, indices[depth - 1]); - _gtk_rbtree_insert_after (tree, tmpnode, max_height); - } - - _gtk_tree_view_update_size (tree_view); - - done: - if (free_path) - gtk_tree_path_free (path); -} - -static void -gtk_tree_view_has_child_toggled (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) +static gboolean +gtk_tree_view_header_focus (GtkTreeView *tree_view, + GtkDirectionType dir) { - GtkTreeView *tree_view = (GtkTreeView *)data; - GtkTreeIter real_iter; - gboolean has_child; - GtkRBTree *tree; - GtkRBNode *node; - gboolean free_path = FALSE; + GtkWidget *focus_child; + GtkContainer *container; - g_return_if_fail (path != NULL || iter != NULL); + GList *last_column, *first_column; + GList *tmp_list; - if (iter) - real_iter = *iter; + if (! GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE)) + return FALSE; - if (path == NULL) - { - path = gtk_tree_model_get_path (model, iter); - free_path = TRUE; - } - else if (iter == NULL) - gtk_tree_model_get_iter (model, &real_iter, path); + focus_child = GTK_CONTAINER (tree_view)->focus_child; + container = GTK_CONTAINER (tree_view); - if (_gtk_tree_view_find_node (tree_view, - path, - &tree, - &node)) - /* We aren't actually showing the node */ - goto done; + for (last_column = g_list_last (tree_view->priv->columns); + last_column && + !(GTK_TREE_VIEW_COLUMN (last_column->data)->visible) && + GTK_WIDGET_CAN_FOCUS (GTK_TREE_VIEW_COLUMN (last_column->data)->button); + last_column = last_column->prev); - if (tree == NULL) - goto done; + for (first_column = tree_view->priv->columns; + first_column && + !(GTK_TREE_VIEW_COLUMN (first_column->data)->visible) && + GTK_WIDGET_CAN_FOCUS (GTK_TREE_VIEW_COLUMN (first_column->data)->button); + first_column = first_column->next); - has_child = gtk_tree_model_iter_has_child (model, &real_iter); - /* Sanity check. + /* No headers are visible, or are focusable. We can't focus in or out. */ - if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT) == has_child) - goto done; - - if (has_child) - GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_PARENT); - else - GTK_RBNODE_UNSET_FLAG (node, GTK_RBNODE_IS_PARENT); + if (last_column == NULL) + return FALSE; - if (has_child && GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IS_LIST)) + switch (dir) { - GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IS_LIST); - if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_SHOW_EXPANDERS)) + case GTK_DIR_TAB_BACKWARD: + case GTK_DIR_TAB_FORWARD: + case GTK_DIR_UP: + case GTK_DIR_DOWN: + if (focus_child == NULL) { - GList *list; + if (tree_view->priv->focus_column != NULL) + focus_child = tree_view->priv->focus_column->button; + else + focus_child = GTK_TREE_VIEW_COLUMN (first_column->data)->button; + gtk_widget_grab_focus (focus_child); + break; + } + return FALSE; - for (list = tree_view->priv->columns; list; list = list->next) - if (GTK_TREE_VIEW_COLUMN (list->data)->visible) - { - GTK_TREE_VIEW_COLUMN (list->data)->dirty = TRUE; - break; - } + case GTK_DIR_LEFT: + case GTK_DIR_RIGHT: + if (focus_child == NULL) + { + if (tree_view->priv->focus_column != NULL) + focus_child = tree_view->priv->focus_column->button; + else if (dir == GTK_DIR_LEFT) + focus_child = GTK_TREE_VIEW_COLUMN (last_column->data)->button; + else + focus_child = GTK_TREE_VIEW_COLUMN (first_column->data)->button; + gtk_widget_grab_focus (focus_child); + break; } - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); - } - else - { - /* FIXME: Just redraw the node */ - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - } - done: - if (free_path) - gtk_tree_path_free (path); -} + if (gtk_container_focus (GTK_CONTAINER (focus_child), dir)) + { + /* The focus moves inside the button. */ + /* This is probably a great example of bad UI */ + break; + } -static void -gtk_tree_view_deleted (GtkTreeModel *model, - GtkTreePath *path, - gpointer data) -{ - GtkTreeView *tree_view = (GtkTreeView *)data; - GtkRBTree *tree; - GtkRBNode *node; - GList *list; + /* We need to move the focus among the row of buttons. */ + for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next) + if (GTK_TREE_VIEW_COLUMN (tmp_list->data)->button == focus_child) + break; - g_return_if_fail (path != NULL); + if (tmp_list == first_column && dir == GTK_DIR_LEFT) + { + focus_child = GTK_TREE_VIEW_COLUMN (last_column->data)->button; + gtk_widget_grab_focus (focus_child); + break; + } + else if (tmp_list == last_column && dir == GTK_DIR_RIGHT) + { + focus_child = GTK_TREE_VIEW_COLUMN (first_column->data)->button; + gtk_widget_grab_focus (focus_child); + break; + } - if (_gtk_tree_view_find_node (tree_view, path, &tree, &node)) - return; + while (tmp_list) + { + GtkTreeViewColumn *column; - if (tree == NULL) - return; + if (dir == GTK_DIR_RIGHT) + tmp_list = tmp_list->next; + else + tmp_list = tmp_list->prev; - gtk_tree_row_reference_deleted (G_OBJECT (data), path); + if (tmp_list == NULL) + { + g_warning ("Internal button not found"); + break; + } + column = tmp_list->data; + if (column->button && + column->visible && + GTK_WIDGET_CAN_FOCUS (column->button)) + { + focus_child = column->button; + gtk_widget_grab_focus (column->button); + break; + } + } + break; + default: + g_assert_not_reached (); + break; + } - /* next, update the selection */ - if (tree_view->priv->anchor) + /* if focus child is non-null, we assume it's been set to the current focus child + */ + if (focus_child) { - GtkTreePath *anchor_path; - - /* the row reference may not have been updated yet. If it has not, - * then anchor_path and path being equal indicates that the anchor - * row was deleted. If it has, then anchor_path == NULL indicates the - * the anchor row was deleted. - */ + for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next) + if (GTK_TREE_VIEW_COLUMN (tmp_list->data)->button == focus_child) + break; - anchor_path = gtk_tree_row_reference_get_path (tree_view->priv->anchor); + tree_view->priv->focus_column = GTK_TREE_VIEW_COLUMN (tmp_list->data); - if (anchor_path == NULL || - gtk_tree_path_compare (path, anchor_path) == 0) + /* If the following isn't true, then the view is smaller then the scrollpane. + */ + if ((focus_child->allocation.x + focus_child->allocation.width) <= + (tree_view->priv->hadjustment->upper)) { - if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) && - tree_view->priv->selection) - gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->selection), - "selection_changed"); + /* Scroll to the button, if needed */ + if ((tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size) < + (focus_child->allocation.x + focus_child->allocation.width)) + gtk_adjustment_set_value (tree_view->priv->hadjustment, + focus_child->allocation.x + focus_child->allocation.width - + tree_view->priv->hadjustment->page_size); + else if (tree_view->priv->hadjustment->value > focus_child->allocation.x) + gtk_adjustment_set_value (tree_view->priv->hadjustment, + focus_child->allocation.x); } - - if (anchor_path) - gtk_tree_path_free (anchor_path); } - for (list = tree_view->priv->columns; list; list = list->next) - if (((GtkTreeViewColumn *)list->data)->visible && - ((GtkTreeViewColumn *)list->data)->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE) - ((GtkTreeViewColumn *)list->data)->dirty = TRUE; + return (focus_child != NULL); +} - /* Ensure we don't have a dangling pointer to a dead node */ - ensure_unprelighted (tree_view); +/* WARNING: Somewhat scary function */ +/* We make the assumption that if container->focus_child != NULL, the focus must + * be in the header. For now, this is accurate. It may not be in the future. + */ - g_assert (tree_view->priv->prelight_node == NULL); +/* The sordid relationship between focus_column and scroll_column: + * + * The focus_column represents the column that currently has keyboard focus, and + * is used when navigating columns by keyboard. scroll_column is used for + * handling scrolling by keyboard, such that in cases. + */ +static gint +gtk_tree_view_focus (GtkContainer *container, + GtkDirectionType direction) +{ + GtkTreeView *tree_view; + GtkWidget *focus_child; + GdkEvent *event; + GtkRBTree *cursor_tree; + GtkRBNode *cursor_node; + GtkTreePath *cursor_path; - if (tree->root->count == 1) - { - if (tree_view->priv->tree == tree) - tree_view->priv->tree = NULL; + g_return_val_if_fail (GTK_IS_TREE_VIEW (container), FALSE); + g_return_val_if_fail (GTK_WIDGET_VISIBLE (container), FALSE); - _gtk_rbtree_remove (tree); - } - else - { - _gtk_rbtree_remove_node (tree, node); - } + tree_view = GTK_TREE_VIEW (container); + + if (!GTK_WIDGET_IS_SENSITIVE (container)) + return FALSE; + + focus_child = container->focus_child; + + /* Case 1. Headers have focus. */ + if (focus_child) + { + switch (direction) + { + case GTK_DIR_LEFT: + case GTK_DIR_RIGHT: + return (gtk_tree_view_header_focus (tree_view, direction)); + case GTK_DIR_TAB_BACKWARD: + case GTK_DIR_UP: + return FALSE; + case GTK_DIR_TAB_FORWARD: + case GTK_DIR_DOWN: - _gtk_tree_view_update_size (GTK_TREE_VIEW (data)); -} + if (tree_view->priv->tree == NULL) + return FALSE; + GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS); + gtk_widget_grab_focus (GTK_WIDGET (container)); -static void -gtk_tree_view_reordered (GtkTreeModel *model, - GtkTreePath *parent, - GtkTreeIter *iter, - gint *new_order, - gpointer data) -{ - GtkTreeView *tree_view = GTK_TREE_VIEW (data); - GtkRBTree *tree; - GtkRBNode *node; - gint len; + if (tree_view->priv->selection == NULL) + tree_view->priv->selection = + _gtk_tree_selection_new_with_tree_view (tree_view); - len = gtk_tree_model_iter_n_children (model, iter); + /* if there is no keyboard focus yet, we select the first node + */ - if (len < 2) - return; + cursor_path = NULL; - gtk_tree_row_reference_reordered (G_OBJECT (data), - parent, - iter, - new_order); + if (tree_view->priv->cursor) + cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); - if (_gtk_tree_view_find_node (tree_view, - parent, - &tree, - &node)) - return; + if (cursor_path == NULL) + { + GtkTreePath *tmp_path = gtk_tree_path_new_root (); - /* We need to special case the parent path */ - if (tree == NULL) - tree = tree_view->priv->tree; - else - tree = node->children; + if (tree_view->priv->cursor) + gtk_tree_row_reference_free (tree_view->priv->cursor); - if (tree == NULL) - return; + tree_view->priv->cursor = + gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, tmp_path); + cursor_path = tmp_path; + } - /* FIXME: we need to unprelight our tree, if it's prelit. */ - _gtk_rbtree_reorder (tree, new_order, len); + gtk_tree_selection_select_path (tree_view->priv->selection, + cursor_path); - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); -} + gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL); -/* Internal tree functions */ -static gint -gtk_tree_view_insert_iter_height (GtkTreeView *tree_view, - GtkRBTree *tree, - GtkTreeIter *iter, - gint depth) -{ - GtkTreeViewColumn *column; - GtkCellRenderer *cell; - GList *list; - gint max_height = 0; - gint i; - gint vertical_separator; + gtk_tree_path_free (cursor_path); - i = 0; + return TRUE; + } + } - gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical_separator", &vertical_separator, NULL); - /* do stuff with node */ - for (list = tree_view->priv->columns; list; list = list->next) + /* Case 2. We don't have focus at all. */ + if (!GTK_WIDGET_HAS_FOCUS (container)) { - gint height = 0, width = 0; - column = list->data; + if ((direction == GTK_DIR_TAB_FORWARD) || + (direction == GTK_DIR_RIGHT) || + (direction == GTK_DIR_DOWN) || + (direction == GTK_DIR_LEFT) || + (tree_view->priv->tree == NULL)) + return gtk_tree_view_header_focus (tree_view, direction); - if (!column->visible) - continue; + /* The headers didn't want the focus, so we take it. */ + GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS); + gtk_widget_grab_focus (GTK_WIDGET (container)); - if (column->column_type == GTK_TREE_VIEW_COLUMN_FIXED) - { - ++i; - continue; - } + if (tree_view->priv->selection == NULL) + tree_view->priv->selection = + _gtk_tree_selection_new_with_tree_view (tree_view); - cell = column->cell; - gtk_tree_view_column_set_cell_data (column, tree_view->priv->model, iter); + cursor_path = NULL; + if (tree_view->priv->cursor) + cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); - gtk_cell_renderer_get_size (cell, GTK_WIDGET (tree_view), NULL, NULL, NULL, &width, &height); - max_height = MAX (max_height, vertical_separator + height); + if (cursor_path == NULL) + { + GtkTreePath *tmp_path = gtk_tree_path_new_root (); - if (i == tree_view->priv->expander_column && - TREE_VIEW_DRAW_EXPANDERS (tree_view)) - gtk_tree_view_column_set_width (column, - MAX (column->width, depth * tree_view->priv->tab_offset + width)); - else - gtk_tree_view_column_set_width (column, - MAX (column->width, width)); + if (tree_view->priv->cursor) + gtk_tree_row_reference_free (tree_view->priv->cursor); - ++i; - } - return max_height; -} + tree_view->priv->cursor = + gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, tmp_path); + cursor_path = tmp_path; + } -static void -gtk_tree_view_build_tree (GtkTreeView *tree_view, - GtkRBTree *tree, - GtkTreeIter *iter, - gint depth, - gboolean recurse, - gboolean calc_bounds) -{ - GtkRBNode *temp = NULL; - gint max_height; + gtk_tree_selection_select_path (tree_view->priv->selection, + cursor_path); - do - { - max_height = 0; - if (calc_bounds) - max_height = gtk_tree_view_insert_iter_height (tree_view, - tree, - iter, - depth); + gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL); - gtk_tree_model_ref_node (tree_view->priv->model, iter); - temp = _gtk_rbtree_insert_after (tree, temp, max_height); - if (recurse) - { - GtkTreeIter child; + gtk_tree_path_free (cursor_path); - if (gtk_tree_model_iter_children (tree_view->priv->model, &child, iter)) - { - temp->children = _gtk_rbtree_new (); - temp->children->parent_tree = tree; - temp->children->parent_node = temp; - gtk_tree_view_build_tree (tree_view, temp->children, &child, depth + 1, recurse, calc_bounds); - } - } - if (gtk_tree_model_iter_has_child (tree_view->priv->model, iter)) - { - if ((temp->flags>K_RBNODE_IS_PARENT) != GTK_RBNODE_IS_PARENT) - temp->flags ^= GTK_RBNODE_IS_PARENT; - GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IS_LIST); - } + return TRUE; } - while (gtk_tree_model_iter_next (tree_view->priv->model, iter)); -} - -static void -gtk_tree_view_calc_size (GtkTreeView *tree_view, - GtkRBTree *tree, - GtkTreeIter *iter, - gint depth) -{ - GtkRBNode *temp; - GtkTreeIter child; - GtkCellRenderer *cell; - GList *list; - GtkTreeViewColumn *column; - gint max_height; - gint vertical_separator; - gint i; - TREE_VIEW_INTERNAL_ASSERT_VOID (tree != NULL); + /* Case 3. We have focus already. */ + if (tree_view->priv->tree == NULL) + return FALSE; - gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical_separator", &vertical_separator, NULL); + if (direction == GTK_DIR_TAB_BACKWARD) + return (gtk_tree_view_header_focus (tree_view, direction)); + else if (direction == GTK_DIR_TAB_FORWARD) + return FALSE; - temp = tree->root; - while (temp->left != tree->nil) - temp = temp->left; + cursor_path = NULL; + if (tree_view->priv->cursor) + cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor); - do + /* Do we have a focus path? We should, unless it was deleted. */ + /* in that case, we pick the first one arbitrarily */ + if (cursor_path == NULL) { - max_height = 0; - /* Do stuff with node */ - for (list = tree_view->priv->columns, i = 0; i < tree_view->priv->n_columns; list = list->next, i++) - { - gint height = 0, width = 0; - column = list->data; + GtkTreePath *tmp_path = gtk_tree_path_new_root (); - if (!column->visible) - continue; + if (tree_view->priv->cursor) + gtk_tree_row_reference_free (tree_view->priv->cursor); - gtk_tree_view_column_set_cell_data (column, tree_view->priv->model, iter); - cell = column->cell; - gtk_cell_renderer_get_size (cell, GTK_WIDGET (tree_view), NULL, NULL, NULL, &width, &height); - max_height = MAX (max_height, vertical_separator + height); + tree_view->priv->cursor = + gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, tmp_path); + cursor_path = tmp_path; - /* FIXME: I'm getting the width of all nodes here. )-: */ - if (column->dirty == FALSE) - continue; + gtk_tree_selection_select_path (tree_view->priv->selection, + cursor_path); - if (column->column_type == GTK_TREE_VIEW_COLUMN_FIXED) - { - continue; - } - if (i == tree_view->priv->expander_column && - TREE_VIEW_DRAW_EXPANDERS (tree_view)) - gtk_tree_view_column_set_width (column, - MAX (column->width, depth * tree_view->priv->tab_offset + width)); - else - gtk_tree_view_column_set_width (column, MAX (column->width, width)); - } + gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL); - _gtk_rbtree_node_set_height (tree, temp, max_height); + gtk_tree_path_free (cursor_path); - if (temp->children != NULL && - gtk_tree_model_iter_children (tree_view->priv->model, &child, iter)) - gtk_tree_view_calc_size (tree_view, temp->children, &child, depth + 1); - temp = _gtk_rbtree_next (tree, temp); + return TRUE; } - while (gtk_tree_model_iter_next (tree_view->priv->model, iter)); -} -static gboolean -gtk_tree_view_discover_dirty_iter (GtkTreeView *tree_view, - GtkTreeIter *iter, - gint depth, - gint *height) -{ - GtkCellRenderer *cell; - GtkTreeViewColumn *column; - GList *list; - gint i; - gboolean retval = FALSE; - gint tmpheight; + /* Case 4. We have focus already. Move the cursor. */ + if (direction == GTK_DIR_LEFT) + { + gdouble val; + val = tree_view->priv->hadjustment->value - tree_view->priv->hadjustment->page_size/2; + val = MAX (val, 0.0); + gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->hadjustment), val); + gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - if (height) - *height = 0; + gtk_tree_path_free (cursor_path); - for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++) + return TRUE; + } + if (direction == GTK_DIR_RIGHT) { - gint width; - column = list->data; - if (column->dirty == TRUE || column->column_type == GTK_TREE_VIEW_COLUMN_FIXED) - continue; - if (!column->visible) - continue; + gdouble val; + val = tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size/2; + val = MIN (tree_view->priv->hadjustment->upper - tree_view->priv->hadjustment->page_size, val); + gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->hadjustment), val); + gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - cell = column->cell; - gtk_tree_view_column_set_cell_data (column, tree_view->priv->model, iter); + gtk_tree_path_free (cursor_path); - if (height) - { - gtk_cell_renderer_get_size (cell, GTK_WIDGET (tree_view), NULL, NULL, NULL, &width, &tmpheight); - *height = MAX (*height, tmpheight); - } - else - { - gtk_cell_renderer_get_size (cell, GTK_WIDGET (tree_view), NULL, NULL, NULL, &width, NULL); - } - if (i == tree_view->priv->expander_column && - TREE_VIEW_DRAW_EXPANDERS (tree_view)) - { - if (depth * tree_view->priv->tab_offset + width > column->width) - { - column->dirty = TRUE; - retval = TRUE; - } - } - else - { - if (width > column->width) - { - column->dirty = TRUE; - retval = TRUE; - } - } + return TRUE; } - return retval; -} + cursor_tree = NULL; + cursor_node = NULL; -static void -gtk_tree_view_discover_dirty (GtkTreeView *tree_view, - GtkRBTree *tree, - GtkTreeIter *iter, - gint depth) -{ - GtkRBNode *temp = tree->root; - GtkTreeViewColumn *column; - GList *list; - GtkTreeIter child; - gboolean is_all_dirty; + _gtk_tree_view_find_node (tree_view, cursor_path, + &cursor_tree, + &cursor_node); - TREE_VIEW_INTERNAL_ASSERT_VOID (tree != NULL); + /* undraw the old row */ + gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL); - while (temp->left != tree->nil) - temp = temp->left; + gtk_tree_path_free (cursor_path); + cursor_path = NULL; - do + if (tree_view->priv->cursor) { - is_all_dirty = TRUE; - for (list = tree_view->priv->columns; list; list = list->next) - { - column = list->data; - if (column->dirty == FALSE) - { - is_all_dirty = FALSE; - break; - } - } - - if (is_all_dirty) - return; + gtk_tree_row_reference_free (tree_view->priv->cursor); + tree_view->priv->cursor = NULL; + } - gtk_tree_view_discover_dirty_iter (tree_view, - iter, - depth, - FALSE); - if (gtk_tree_model_iter_children (tree_view->priv->model, &child, iter) && - temp->children != NULL) - gtk_tree_view_discover_dirty (tree_view, temp->children, &child, depth + 1); - temp = _gtk_rbtree_next (tree, temp); + switch (direction) + { + case GTK_DIR_TAB_BACKWARD: + case GTK_DIR_UP: + _gtk_rbtree_prev_full (cursor_tree, + cursor_node, + &cursor_tree, + &cursor_node); + break; + case GTK_DIR_TAB_FORWARD: + case GTK_DIR_DOWN: + _gtk_rbtree_next_full (cursor_tree, + cursor_node, + &cursor_tree, + &cursor_node); + break; + default: + break; } - while (gtk_tree_model_iter_next (tree_view->priv->model, iter)); -} + if (cursor_node) + { + GdkModifierType state = 0; -static void -gtk_tree_view_check_dirty (GtkTreeView *tree_view) -{ - GtkTreePath *path; - gboolean dirty = FALSE; - GList *list; - GtkTreeViewColumn *column; - GtkTreeIter iter; + event = gtk_get_current_event (); + if (event) + gdk_event_get_state (event, &state); - if (!GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_MODEL_SETUP) && - tree_view->priv->model) - gtk_tree_view_setup_model (tree_view); + if (event) + gdk_event_free (event); - for (list = tree_view->priv->columns; list; list = list->next) - { - column = list->data; - if (column->dirty) - { - dirty = TRUE; - if (column->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE) - { - gint w = 1; + cursor_path = _gtk_tree_view_find_path (tree_view, + cursor_tree, + cursor_node); - if (column->button) - w = MAX (w, column->button->requisition.width); + if (cursor_path) + { + _gtk_tree_selection_internal_select_node (tree_view->priv->selection, + cursor_node, + cursor_tree, + cursor_path, + state); - gtk_tree_view_column_set_width (column, w); - } - } - } + tree_view->priv->cursor = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, cursor_path); - if (dirty == FALSE) - return; - if (tree_view->priv->model == NULL) - return; + /* draw the newly-selected row */ + gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL); - path = gtk_tree_path_new_root (); - if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, path)) - { - gtk_tree_view_calc_size (tree_view, tree_view->priv->tree, &iter, 1); - _gtk_tree_view_update_size (tree_view); - } + gtk_tree_path_free (cursor_path); + } - gtk_tree_path_free (path); + gtk_tree_view_clamp_node_visible (tree_view, cursor_tree, cursor_node); + gtk_widget_grab_focus (GTK_WIDGET (tree_view)); - for (list = tree_view->priv->columns; list; list = list->next) - { - column = list->data; - column->dirty = FALSE; + return TRUE; } -} -/* Make sure the node is visible vertically */ -static void -gtk_tree_view_clamp_node_visible (GtkTreeView *tree_view, - GtkRBTree *tree, - GtkRBNode *node) -{ - gint offset; + /* At this point, we've progressed beyond the edge of the rows. */ - offset = _gtk_rbtree_node_find_offset (tree, node); + if (direction == GTK_DIR_UP) + /* We can't go back anymore. Try the headers */ + return (gtk_tree_view_header_focus (tree_view, direction)); - /* we reverse the order, b/c in the unusual case of the - * node's height being taller then the visible area, we'd rather - * have the node flush to the top - */ - if (offset + GTK_RBNODE_GET_HEIGHT (node) > - tree_view->priv->vadjustment->value + tree_view->priv->vadjustment->page_size) - gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), - offset + GTK_RBNODE_GET_HEIGHT (node) - - tree_view->priv->vadjustment->page_size); - if (offset < tree_view->priv->vadjustment->value) - gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), - offset); + /* we've reached the end of the tree. Go on. */ + return FALSE; } -/* This function could be more efficient. - * I'll optimize it if profiling seems to imply that - * it's important - */ -GtkTreePath * -_gtk_tree_view_find_path (GtkTreeView *tree_view, - GtkRBTree *tree, - GtkRBNode *node) -{ - GtkTreePath *path; - GtkRBTree *tmp_tree; - GtkRBNode *tmp_node, *last; - gint count; - - path = gtk_tree_path_new (); - - g_return_val_if_fail (node != NULL, path); - g_return_val_if_fail (node != tree->nil, path); - count = 1 + node->left->count; +static void +gtk_tree_view_set_focus_child (GtkContainer *container, + GtkWidget *child) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (container); + GList *list; - last = node; - tmp_node = node->parent; - tmp_tree = tree; - while (tmp_tree) + for (list = tree_view->priv->columns; list; list = list->next) { - while (tmp_node != tmp_tree->nil) - { - if (tmp_node->right == last) - count += 1 + tmp_node->left->count; - last = tmp_node; - tmp_node = tmp_node->parent; - } - gtk_tree_path_prepend_index (path, count - 1); - last = tmp_tree->parent_node; - tmp_tree = tmp_tree->parent_tree; - if (last) + if (GTK_TREE_VIEW_COLUMN (list->data)->button == child) { - count = 1 + last->left->count; - tmp_node = last->parent; + tree_view->priv->focus_column = GTK_TREE_VIEW_COLUMN (list->data); + break; } } - return path; + + (* parent_class->set_focus_child) (container, child); } -/* Returns TRUE if we ran out of tree before finding the path. - */ -gboolean -_gtk_tree_view_find_node (GtkTreeView *tree_view, - GtkTreePath *path, - GtkRBTree **tree, - GtkRBNode **node) +static void +gtk_tree_view_set_adjustments (GtkTreeView *tree_view, + GtkAdjustment *hadj, + GtkAdjustment *vadj) { - GtkRBNode *tmpnode = NULL; - GtkRBTree *tmptree = tree_view->priv->tree; - gint *indices = gtk_tree_path_get_indices (path); - gint depth = gtk_tree_path_get_depth (path); - gint i = 0; + gboolean need_adjust = FALSE; - *node = NULL; - *tree = NULL; + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - if (depth == 0) - return FALSE; - do + if (hadj) + g_return_if_fail (GTK_IS_ADJUSTMENT (hadj)); + else + hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0)); + if (vadj) + g_return_if_fail (GTK_IS_ADJUSTMENT (vadj)); + else + vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0)); + + if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj)) { - if (tmptree == NULL) - { - *node = tmpnode; - *tree = tmptree; - return TRUE; - } - tmpnode = _gtk_rbtree_find_count (tmptree, indices[i] + 1); - ++i; - if (i >= depth) - { - *node = tmpnode; - *tree = tmptree; - return FALSE; - } - tmptree = tmpnode->children; + gtk_signal_disconnect_by_data (GTK_OBJECT (tree_view->priv->hadjustment), tree_view); + gtk_object_unref (GTK_OBJECT (tree_view->priv->hadjustment)); } - while (1); -} -static void -gtk_tree_view_unref_tree_helper (GtkTreeModel *model, - GtkTreeIter *iter, - GtkRBTree *tree, - GtkRBNode *node) -{ - do + if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj)) { - g_return_if_fail (node != NULL); - - if (node->children) - { - GtkTreeIter child; - GtkRBTree *new_tree; - GtkRBNode *new_node; + gtk_signal_disconnect_by_data (GTK_OBJECT (tree_view->priv->vadjustment), tree_view); + gtk_object_unref (GTK_OBJECT (tree_view->priv->vadjustment)); + } - new_tree = node->children; - new_node = new_tree->root; + if (tree_view->priv->hadjustment != hadj) + { + tree_view->priv->hadjustment = hadj; + gtk_object_ref (GTK_OBJECT (tree_view->priv->hadjustment)); + gtk_object_sink (GTK_OBJECT (tree_view->priv->hadjustment)); - while (new_node && new_node->left != new_tree->nil) - new_node = new_node->left; + gtk_signal_connect (GTK_OBJECT (tree_view->priv->hadjustment), "value_changed", + (GtkSignalFunc) gtk_tree_view_adjustment_changed, + tree_view); + need_adjust = TRUE; + } - g_return_if_fail (gtk_tree_model_iter_children (model, &child, iter)); - gtk_tree_view_unref_tree_helper (model, &child, new_tree, new_node); - } + if (tree_view->priv->vadjustment != vadj) + { + tree_view->priv->vadjustment = vadj; + gtk_object_ref (GTK_OBJECT (tree_view->priv->vadjustment)); + gtk_object_sink (GTK_OBJECT (tree_view->priv->vadjustment)); - gtk_tree_model_unref_node (model, iter); - node = _gtk_rbtree_next (tree, node); + gtk_signal_connect (GTK_OBJECT (tree_view->priv->vadjustment), "value_changed", + (GtkSignalFunc) gtk_tree_view_adjustment_changed, + tree_view); + need_adjust = TRUE; } - while (gtk_tree_model_iter_next (model, iter)); + + if (need_adjust) + gtk_tree_view_adjustment_changed (NULL, tree_view); } + +/* TreeModel Callbacks + */ + static void -gtk_tree_view_unref_tree (GtkTreeView *tree_view, - GtkRBTree *tree) +gtk_tree_view_range_changed (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + GtkTreePath *end_path, + GtkTreeIter *end_iter, + gpointer data) { - GtkTreeIter iter; - GtkTreePath *path; + GtkTreeView *tree_view = (GtkTreeView *)data; + GtkRBTree *tree; GtkRBNode *node; + gint height; + gboolean dirty_marked; + gboolean free_path = FALSE; + gint vertical_separator; - node = tree->root; - while (node && node->left != tree->nil) - node = node->left; + g_return_if_fail (path != NULL || iter != NULL); - g_return_if_fail (node != NULL); - path = _gtk_tree_view_find_path (tree_view, tree, node); - gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_view->priv->model), - &iter, path); - gtk_tree_view_unref_tree_helper (GTK_TREE_MODEL (tree_view->priv->model), &iter, tree, node); - gtk_tree_path_free (path); -} + gtk_widget_style_get (GTK_WIDGET (data), "vertical_separator", &vertical_separator, NULL); -static void -gtk_tree_view_set_column_drag_info (GtkTreeView *tree_view, - GtkTreeViewColumn *column) -{ - GtkTreeViewColumn *left_column; - GtkTreeViewColumn *cur_column; - GtkTreeViewColumnReorder *reorder; + if (path == NULL) + { + path = gtk_tree_model_get_path (model, iter); + free_path = TRUE; + } + else if (iter == NULL) + gtk_tree_model_get_iter (model, iter, path); - GList *tmp_list; - gint left; + if (_gtk_tree_view_find_node (tree_view, + path, + &tree, + &node)) + /* We aren't actually showing the node */ + goto done; - /* We want to precalculate the motion list such that we know what column slots - * are available. - */ - left_column = NULL; + if (tree == NULL) + goto done; - /* First, identify all possible drop spots */ - tmp_list = tree_view->priv->columns; + dirty_marked = gtk_tree_view_discover_dirty_iter (tree_view, + iter, + gtk_tree_path_get_depth (path), + &height); - while (tmp_list) + if (GTK_RBNODE_GET_HEIGHT (node) != height + vertical_separator) { - g_assert (tmp_list); + _gtk_rbtree_node_set_height (tree, node, height + vertical_separator); + gtk_widget_queue_resize (GTK_WIDGET (data)); + goto done; + } + if (dirty_marked) + gtk_widget_queue_resize (GTK_WIDGET (data)); + else + { + gtk_tree_view_queue_draw_node (tree_view, tree, node, NULL); + } - cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data); - tmp_list = tmp_list->next; + done: + if (free_path) + gtk_tree_path_free (path); +} - if (cur_column->visible == FALSE) - continue; +static void +gtk_tree_view_inserted (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + GtkTreeView *tree_view = (GtkTreeView *) data; + gint *indices; + GtkRBTree *tmptree, *tree; + GtkRBNode *tmpnode = NULL; + gint max_height; + gint depth; + gint i = 0; + gboolean free_path = FALSE; - reorder = g_new (GtkTreeViewColumnReorder, 1); - reorder->left_column = left_column; - left_column = reorder->right_column = cur_column; + if (tree_view->priv->tree == NULL) + tree_view->priv->tree = _gtk_rbtree_new (); - tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder); - } + tmptree = tree = tree_view->priv->tree; + g_return_if_fail (path != NULL || iter != NULL); - /* Add the last one */ - reorder = g_new (GtkTreeViewColumnReorder, 1); - reorder->left_column = left_column; - reorder->right_column = NULL; - tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder); + if (path == NULL) + { + path = gtk_tree_model_get_path (model, iter); + free_path = TRUE; + } + else if (iter == NULL) + gtk_tree_model_get_iter (model, iter, path); - /* Now we want to fill in the ranges for the columns, now that we've isolated them */ - left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view); + /* Update all row-references */ + gtk_tree_row_reference_inserted (G_OBJECT (data), path); - for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next) + depth = gtk_tree_path_get_depth (path); + indices = gtk_tree_path_get_indices (path); + + /* First, find the parent tree */ + while (i < depth - 1) { - reorder = (GtkTreeViewColumnReorder *) tmp_list->data; + if (tmptree == NULL) + { + /* We aren't showing the node */ + goto done; + } - reorder->left_align = left; - if (tmp_list->next != NULL) + tmpnode = _gtk_rbtree_find_count (tmptree, indices[i] + 1); + if (tmpnode == NULL) { - g_assert (tmp_list->next->data); - left = reorder->right_align = (reorder->right_column->button->allocation.x + - reorder->right_column->button->allocation.width + - ((GtkTreeViewColumnReorder *)tmp_list->next->data)->left_column->button->allocation.x)/2; + g_warning ("A node was inserted with a parent that's not in the tree.\n" \ + "This possibly means that a GtkTreeModel inserted a child node\n" \ + "before the parent was inserted."); + goto done; } - else + else if (!GTK_RBNODE_FLAG_SET (tmpnode, GTK_RBNODE_IS_PARENT)) { - gint width; - - gdk_window_get_size (tree_view->priv->header_window, &width, NULL); - reorder->right_align = width + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view); + /* FIXME enforce correct behavior on model, probably */ + /* In theory, the model should have emitted has_child_toggled here. We + * try to catch it anyway, just to be safe, in case the model hasn't. + */ + GtkTreePath *tmppath = _gtk_tree_view_find_path (tree_view, + tree, + tmpnode); + gtk_tree_view_has_child_toggled (model, tmppath, NULL, data); + gtk_tree_path_free (tmppath); + goto done; } + + tmptree = tmpnode->children; + tree = tmptree; + i++; + } + + if (tree == NULL) + goto done; + + /* ref the node */ + gtk_tree_model_ref_node (tree_view->priv->model, iter); + max_height = gtk_tree_view_insert_iter_height (tree_view, + tree, + iter, + depth); + if (indices[depth - 1] == 0) + { + tmpnode = _gtk_rbtree_find_count (tree, 1); + _gtk_rbtree_insert_before (tree, tmpnode, max_height); } + else + { + tmpnode = _gtk_rbtree_find_count (tree, indices[depth - 1]); + _gtk_rbtree_insert_after (tree, tmpnode, max_height); + } + + _gtk_tree_view_update_size (tree_view); + + done: + if (free_path) + gtk_tree_path_free (path); } -void -_gtk_tree_view_column_start_drag (GtkTreeView *tree_view, - GtkTreeViewColumn *column) +static void +gtk_tree_view_has_child_toggled (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) { - GdkEvent send_event; - GtkAllocation allocation; - gint x, y, width, height; - - g_return_if_fail (tree_view->priv->column_drag_info == NULL); + GtkTreeView *tree_view = (GtkTreeView *)data; + GtkTreeIter real_iter; + gboolean has_child; + GtkRBTree *tree; + GtkRBNode *node; + gboolean free_path = FALSE; - gtk_tree_view_set_column_drag_info (tree_view, column); + g_return_if_fail (path != NULL || iter != NULL); - if (tree_view->priv->column_drag_info == NULL) - return; + if (iter) + real_iter = *iter; - if (tree_view->priv->drag_window == NULL) + if (path == NULL) { - GdkWindowAttr attributes; - guint attributes_mask; - - attributes.window_type = GDK_WINDOW_CHILD; - attributes.wclass = GDK_INPUT_OUTPUT; - attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view)); - attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view)); - attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK; - attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; + path = gtk_tree_model_get_path (model, iter); + free_path = TRUE; + } + else if (iter == NULL) + gtk_tree_model_get_iter (model, &real_iter, path); - tree_view->priv->drag_window = gdk_window_new (tree_view->priv->bin_window, - &attributes, - attributes_mask); - gdk_window_set_user_data (tree_view->priv->drag_window, GTK_WIDGET (tree_view)); + if (_gtk_tree_view_find_node (tree_view, + path, + &tree, + &node)) + /* We aren't actually showing the node */ + goto done; - attributes.window_type = GDK_WINDOW_TEMP; - tree_view->priv->drag_highlight_window = gdk_window_new (NULL, &attributes, attributes_mask); - gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view)); + if (tree == NULL) + goto done; - tree_view->priv->drag_header_window = gdk_window_new (NULL, &attributes, attributes_mask); - gdk_window_set_user_data (tree_view->priv->drag_header_window, GTK_WIDGET (tree_view)); - } - - gdk_pointer_ungrab (GDK_CURRENT_TIME); - gdk_keyboard_ungrab (GDK_CURRENT_TIME); + has_child = gtk_tree_model_iter_has_child (model, &real_iter); + /* Sanity check. + */ + if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT) == has_child) + goto done; - gtk_grab_remove (column->button); + if (has_child) + GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_PARENT); + else + GTK_RBNODE_UNSET_FLAG (node, GTK_RBNODE_IS_PARENT); - send_event.crossing.type = GDK_LEAVE_NOTIFY; - send_event.crossing.send_event = TRUE; - send_event.crossing.window = column->button->window; - send_event.crossing.subwindow = NULL; - send_event.crossing.detail = GDK_NOTIFY_ANCESTOR; - send_event.crossing.time = GDK_CURRENT_TIME; + if (has_child && GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_IS_LIST)) + { + GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IS_LIST); + if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_SHOW_EXPANDERS)) + { + GList *list; - gtk_propagate_event (column->button, &send_event); + for (list = tree_view->priv->columns; list; list = list->next) + if (GTK_TREE_VIEW_COLUMN (list->data)->visible) + { + GTK_TREE_VIEW_COLUMN (list->data)->dirty = TRUE; + break; + } + } + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); + } + else + { + /* FIXME: Just redraw the node */ + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + } - send_event.button.type = GDK_BUTTON_RELEASE; - send_event.button.window = GDK_ROOT_PARENT (); - send_event.button.send_event = TRUE; - send_event.button.time = GDK_CURRENT_TIME; - send_event.button.x = -1; - send_event.button.y = -1; - send_event.button.axes = NULL; - send_event.button.state = 0; - send_event.button.button = 1; - send_event.button.device = gdk_core_pointer; - send_event.button.x_root = 0; - send_event.button.y_root = 0; + done: + if (free_path) + gtk_tree_path_free (path); +} - gtk_propagate_event (column->button, &send_event); +static void +gtk_tree_view_deleted (GtkTreeModel *model, + GtkTreePath *path, + gpointer data) +{ + GtkTreeView *tree_view = (GtkTreeView *)data; + GtkRBTree *tree; + GtkRBNode *node; + GList *list; - gdk_window_move_resize (tree_view->priv->drag_window, - column->button->allocation.x, - column->button->allocation.y + column->button->allocation.height, - column->button->allocation.width, - column->button->allocation.height); - gdk_window_reparent (column->button->window, tree_view->priv->drag_window, 0, 0); - tree_view->priv->drag_column_x = column->button->allocation.x; - allocation = column->button->allocation; - allocation.x = 0; - gtk_widget_size_allocate (column->button, &allocation); - gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window); + g_return_if_fail (path != NULL); + if (_gtk_tree_view_find_node (tree_view, path, &tree, &node)) + return; - tree_view->priv->drag_column = column; - gdk_window_show (tree_view->priv->drag_window); + if (tree == NULL) + return; - gdk_window_get_origin (tree_view->priv->header_window, &x, &y); - gdk_window_get_size (tree_view->priv->header_window, &width, &height); - gdk_window_move_resize (tree_view->priv->drag_header_window, x, y, width, height); - gdk_window_reparent (tree_view->priv->header_window, tree_view->priv->drag_header_window, 0, 0); - gdk_window_show (tree_view->priv->drag_header_window); + gtk_tree_row_reference_deleted (G_OBJECT (data), path); - while (gtk_events_pending ()) - gtk_main_iteration (); + /* next, update the selection */ + if (tree_view->priv->anchor) + { + GtkTreePath *anchor_path; - GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_IN_COLUMN_DRAG); - gdk_pointer_grab (tree_view->priv->drag_window, - FALSE, - GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK, - NULL, NULL, GDK_CURRENT_TIME); + /* the row reference may not have been updated yet. If it has not, + * then anchor_path and path being equal indicates that the anchor + * row was deleted. If it has, then anchor_path == NULL indicates the + * the anchor row was deleted. + */ -} + anchor_path = gtk_tree_row_reference_get_path (tree_view->priv->anchor); -static void -gtk_tree_view_queue_draw_node (GtkTreeView *tree_view, - GtkRBTree *tree, - GtkRBNode *node, - GdkRectangle *clip_rect) -{ - GdkRectangle rect; + if (anchor_path == NULL || + gtk_tree_path_compare (path, anchor_path) == 0) + { + if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) && + tree_view->priv->selection) + gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->selection), + "selection_changed"); + } - if (!GTK_WIDGET_REALIZED (tree_view)) - return; + if (anchor_path) + gtk_tree_path_free (anchor_path); + } - rect.x = 0; - rect.width = tree_view->priv->width; + for (list = tree_view->priv->columns; list; list = list->next) + if (((GtkTreeViewColumn *)list->data)->visible && + ((GtkTreeViewColumn *)list->data)->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE) + ((GtkTreeViewColumn *)list->data)->dirty = TRUE; - rect.y = BACKGROUND_FIRST_PIXEL (tree_view, tree, node); - rect.height = BACKGROUND_HEIGHT (node); + /* Ensure we don't have a dangling pointer to a dead node */ + ensure_unprelighted (tree_view); - if (clip_rect) - { - GdkRectangle new_rect; + g_assert (tree_view->priv->prelight_node == NULL); - gdk_rectangle_intersect (clip_rect, &rect, &new_rect); + if (tree->root->count == 1) + { + if (tree_view->priv->tree == tree) + tree_view->priv->tree = NULL; - gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE); + _gtk_rbtree_remove (tree); } else { - gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE); + _gtk_rbtree_remove_node (tree, node); } -} - -static void -gtk_tree_view_queue_draw_path (GtkTreeView *tree_view, - GtkTreePath *path, - GdkRectangle *clip_rect) -{ - GtkRBTree *tree = NULL; - GtkRBNode *node = NULL; - - _gtk_tree_view_find_node (tree_view, path, &tree, &node); - if (tree) - gtk_tree_view_queue_draw_node (tree_view, tree, node, clip_rect); + _gtk_tree_view_update_size (GTK_TREE_VIEW (data)); } -/* x and y are the mouse position - */ + static void -gtk_tree_view_draw_arrow (GtkTreeView *tree_view, - GtkRBTree *tree, - GtkRBNode *node, - gint x, - gint y) +gtk_tree_view_reordered (GtkTreeModel *model, + GtkTreePath *parent, + GtkTreeIter *iter, + gint *new_order, + gpointer data) { - GdkRectangle area; - GtkStateType state; - GtkWidget *widget; - gint x_offset = 0; - gint vertical_separator; - gint expander_height; + GtkTreeView *tree_view = GTK_TREE_VIEW (data); + GtkRBTree *tree; + GtkRBNode *node; + gint len; - gtk_widget_style_get (GTK_WIDGET (tree_view), - "vertical_separator", &vertical_separator, - "expander_height", &expander_height, - NULL); + len = gtk_tree_model_iter_n_children (model, iter); - if (! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT)) + if (len < 2) return; - widget = GTK_WIDGET (tree_view); - - gtk_tree_view_get_arrow_xrange (tree_view, &x_offset, NULL); + gtk_tree_row_reference_reordered (G_OBJECT (data), + parent, + iter, + new_order); - area.x = x_offset; - area.y = CELL_FIRST_PIXEL (tree_view, tree, node, vertical_separator); - area.width = tree_view->priv->tab_offset - 2; - area.height = CELL_HEIGHT (node, vertical_separator); + if (_gtk_tree_view_find_node (tree_view, + parent, + &tree, + &node)) + return; - if (node == tree_view->priv->button_pressed_node) - { - if (x >= area.x && x <= (area.x + area.width) && - y >= area.y && y <= (area.y + area.height)) - state = GTK_STATE_ACTIVE; - else - state = GTK_STATE_NORMAL; - } + /* We need to special case the parent path */ + if (tree == NULL) + tree = tree_view->priv->tree; else - { - if (node == tree_view->priv->prelight_node && - GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT)) - state = GTK_STATE_PRELIGHT; - else - state = GTK_STATE_NORMAL; - } - - gtk_paint_expander (widget->style, - tree_view->priv->bin_window, - state, - &area, - widget, - "treeview", - area.x, - (area.y + (area.height - expander_height) / 2 - (area.height + 1) % 2), - node->children != NULL); -} + tree = node->children; + if (tree == NULL) + return; -static void -_gtk_tree_view_update_col_width (GtkTreeView *tree_view) -{ - GList *list, *last_column; - GtkTreeViewColumn *column; - gint width = 0; + /* FIXME: we need to unprelight our tree, if it's prelit. */ + _gtk_rbtree_reorder (tree, new_order, len); - for (last_column = g_list_last (tree_view->priv->columns); - last_column && - !(GTK_TREE_VIEW_COLUMN (last_column->data)->visible) && - GTK_WIDGET_CAN_FOCUS (GTK_TREE_VIEW_COLUMN (last_column->data)->button); - last_column = last_column->prev) - ; + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); +} - if (last_column == NULL) - return; - for (list = tree_view->priv->columns; list != last_column; list = list->next) - { - column = GTK_TREE_VIEW_COLUMN (list->data); - if (! column->visible) - continue; +/* Internal tree functions + */ - width += column->width; - column->displayed_width = (CLAMP (column->width, (column->min_width!=-1)?column->min_width:column->width, (column->max_width!=-1)?column->max_width:column->width)); - } - column = GTK_TREE_VIEW_COLUMN (last_column->data); - column->displayed_width = MAX (GTK_WIDGET (tree_view)->allocation.width, tree_view->priv->width) - width; -} -void -_gtk_tree_view_update_size (GtkTreeView *tree_view) +static void +gtk_tree_view_get_background_xrange (GtkTreeView *tree_view, + GtkRBTree *tree, + GtkTreeViewColumn *column, + gint *x1, + gint *x2) { - gint width, height; + GtkTreeViewColumn *tmp_column = NULL; + gint total_width; GList *list; - GtkTreeViewColumn *column; - gint vertical_separator; - gint i; - gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical_separator", &vertical_separator, NULL); + if (x1) + *x1 = 0; - if (tree_view->priv->model == NULL) - { - tree_view->priv->width = 0; - tree_view->priv->height = 0; - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - return; - } + if (x2) + *x2 = 0; - width = 0; - for (list = tree_view->priv->columns, i = 0; list; list = list->next, i++) + total_width = 0; + for (list = tree_view->priv->columns; list; list = list->next) { - column = list->data; - if (!column->visible) - continue; - width += TREE_VIEW_COLUMN_WIDTH (column); - } + tmp_column = list->data; - if (tree_view->priv->tree == NULL) - height = 0; - else - height = tree_view->priv->tree->root->offset + vertical_separator; + if (tmp_column == column) + break; - if (tree_view->priv->width != width) - { - tree_view->priv->width = width; - tree_view->priv->hadjustment->upper = width; - gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->hadjustment), "changed"); + if (tmp_column->visible) + total_width += tmp_column->width; } - if (tree_view->priv->height != height) + if (tmp_column != column) { - tree_view->priv->height = height; - tree_view->priv->vadjustment->upper = tree_view->priv->height; - gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->vadjustment), "changed"); + g_warning (G_STRLOC": passed-in column isn't in the tree"); + return; } - if (GTK_WIDGET_REALIZED (tree_view)) - { - gdk_window_resize (tree_view->priv->bin_window, MAX (width, GTK_WIDGET (tree_view)->allocation.width), height + TREE_VIEW_HEADER_HEIGHT (tree_view)); - gdk_window_resize (tree_view->priv->header_window, MAX (width, GTK_WIDGET (tree_view)->allocation.width), tree_view->priv->header_height); + if (x1) + *x1 = total_width; - _gtk_tree_view_update_col_width (tree_view); + if (x2) + { + if (column->visible) + *x2 = total_width + column->width; + else + *x2 = total_width; /* width of 0 */ } - - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); } -/* this function returns the new width of the column being resized given - * the column and x position of the cursor; the x cursor position is passed - * in as a pointer and automagicly corrected if it's beyond min/max limits - */ -static gint -gtk_tree_view_new_column_width (GtkTreeView *tree_view, - gint i, - gint *x) -{ - GtkTreeViewColumn *column; - gint width; - - /* first translate the x position from widget->window - * to clist->clist_window - */ - - column = g_list_nth (tree_view->priv->columns, i)->data; - width = *x - column->button->allocation.x; - - /* Clamp down the value */ - if (column->min_width == -1) - width = MAX (column->button->requisition.width, - width); - else - width = MAX (column->min_width, - width); - if (column->max_width != -1) - width = MIN (width, column->max_width != -1); - *x = column->button->allocation.x + width; +static void +gtk_tree_view_get_cell_xrange (GtkTreeView *tree_view, + GtkRBTree *tree, + GtkTreeViewColumn *column, + gint *x1, + gint *x2) +{ + GtkTreeViewColumn *tmp_column = NULL; + gint total_width; + GList *list; + gint i; - return width; -} + if (x1) + *x1 = 0; -/* Callbacks */ -static void -gtk_tree_view_adjustment_changed (GtkAdjustment *adjustment, - GtkTreeView *tree_view) -{ - if (GTK_WIDGET_REALIZED (tree_view)) + if (x2) + *x2 = 0; + + i = 0; + total_width = 0; + for (list = tree_view->priv->columns; list; list = list->next) { - gdk_window_move (tree_view->priv->bin_window, - - tree_view->priv->hadjustment->value, - - tree_view->priv->vadjustment->value); - gdk_window_move (tree_view->priv->header_window, - - tree_view->priv->hadjustment->value, - 0); + tmp_column = list->data; - gdk_window_process_updates (tree_view->priv->bin_window, TRUE); - gdk_window_process_updates (tree_view->priv->header_window, TRUE); + if (tmp_column == column) + break; + + if (tmp_column->visible) + total_width += tmp_column->width; + + ++i; } -} - + if (tmp_column != column) + { + g_warning (G_STRLOC": passed-in column isn't in the tree"); + return; + } -/* Public methods - */ + /* Remember we're getting the cell range, i.e. the cell_area passed + * to the cell renderer. + */ -/** - * gtk_tree_view_new: - * - * Creates a new #GtkTreeView widget. - * - * Return value: A newly created #GtkTreeView widget. - **/ -GtkWidget * -gtk_tree_view_new (void) -{ - GtkTreeView *tree_view; + if (i == tree_view->priv->expander_column) + total_width += tree_view->priv->tab_offset * _gtk_rbtree_get_depth (tree); - tree_view = GTK_TREE_VIEW (gtk_type_new (gtk_tree_view_get_type ())); + if (x1) + *x1 = total_width; - return GTK_WIDGET (tree_view); + if (x2) + { + if (column->visible) + *x2 = total_width + column->displayed_width; + else + *x2 = total_width; /* width of 0 */ + } } -/** - * gtk_tree_view_new_with_model: - * @model: the model. - * - * Creates a new #GtkTreeView widget with the model initialized to @model. - * - * Return value: A newly created #GtkTreeView widget. - **/ -GtkWidget * -gtk_tree_view_new_with_model (GtkTreeModel *model) +static void +gtk_tree_view_get_arrow_xrange (GtkTreeView *tree_view, + gint *x1, + gint *x2) { - GtkTreeView *tree_view; + gint x_offset = 0; + GList *list; + GtkTreeViewColumn *tmp_column = NULL; + gint total_width; + gint i; - tree_view = GTK_TREE_VIEW (gtk_type_new (gtk_tree_view_get_type ())); - gtk_tree_view_set_model (tree_view, model); + i = 0; + total_width = 0; + for (list = tree_view->priv->columns; list; list = list->next) + { + tmp_column = list->data; - return GTK_WIDGET (tree_view); -} + if (i == tree_view->priv->expander_column) + { + x_offset = total_width; + break; + } -/** - * gtk_tree_view_get_model: - * @tree_view: a #GtkTreeView - * - * Returns the model the the #GtkTreeView is based on. Returns NULL if the - * model is unset. - * - * Return value: A #GtkTreeModel, or NULL if none is currently being used. - **/ -GtkTreeModel * -gtk_tree_view_get_model (GtkTreeView *tree_view) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); + if (tmp_column->visible) + total_width += tmp_column->width; - return tree_view->priv->model; + ++i; + } + + if (x1) + *x1 = x_offset; + + if (tmp_column && tmp_column->visible) + { + /* +1 because x2 isn't included in the range. */ + if (x2) + *x2 = x_offset + tree_view->priv->tab_offset + 1; + } + else + { + /* return an empty range, the expander column is hidden */ + if (x2) + *x2 = x_offset; + } } static void @@ -4435,1452 +4469,1269 @@ gtk_tree_view_setup_model (GtkTreeView *tree_view) gtk_tree_path_free (path); - // gtk_tree_view_create_buttons (tree_view); + /* FIXME: do I need to do this? gtk_tree_view_create_buttons (tree_view); */ GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_MODEL_SETUP); } -/** - * gtk_tree_view_set_model: - * @tree_view: A #GtkTreeNode. - * @model: The model. - * - * Sets the model for a #GtkTreeView. If the @tree_view already has a model - * set, it will remove it before setting the new model. If @model is NULL, then - * it will unset the old model. - **/ -void -gtk_tree_view_set_model (GtkTreeView *tree_view, - GtkTreeModel *model) -{ - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - if (model == tree_view->priv->model) - return; - - if (model != NULL) - g_object_ref (model); - - if (tree_view->priv->model != NULL) - { - if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_MODEL_SETUP)) - { - g_signal_handlers_disconnect_matched (G_OBJECT (tree_view->priv->model), - G_SIGNAL_MATCH_DATA, - 0, 0, NULL, - NULL, tree_view); - g_signal_handlers_disconnect_matched (G_OBJECT (tree_view->priv->model), - G_SIGNAL_MATCH_DATA, - 0, 0, NULL, - NULL, tree_view); - g_signal_handlers_disconnect_matched (G_OBJECT (tree_view->priv->model), - G_SIGNAL_MATCH_DATA, - 0, 0, NULL, - NULL, tree_view); - g_signal_handlers_disconnect_matched (G_OBJECT (tree_view->priv->model), - G_SIGNAL_MATCH_DATA, - 0, 0, NULL, - NULL, tree_view); - g_signal_handlers_disconnect_matched (G_OBJECT (tree_view->priv->model), - G_SIGNAL_MATCH_DATA, - 0, 0, NULL, - NULL, tree_view); - _gtk_rbtree_free (tree_view->priv->tree); - } - - if (tree_view->priv->drag_dest_row) - gtk_tree_row_reference_free (tree_view->priv->drag_dest_row); - - GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_MODEL_SETUP); - g_object_unref (tree_view->priv->model); - } - - tree_view->priv->model = model; - - if (model == NULL) - { - tree_view->priv->tree = NULL; - if (GTK_WIDGET_REALIZED (tree_view)) - _gtk_tree_view_update_size (tree_view); - } - else if (GTK_WIDGET_REALIZED (tree_view)) - { - gtk_tree_view_setup_model (tree_view); - _gtk_tree_view_update_size (tree_view); - } - - g_object_notify (G_OBJECT (tree_view), "model"); -} - -/** - * gtk_tree_view_get_selection: - * @tree_view: A #GtkTreeView. - * - * Gets the #GtkTreeSelection associated with @tree_view. - * - * Return value: A #GtkTreeSelection object. - **/ -GtkTreeSelection * -gtk_tree_view_get_selection (GtkTreeView *tree_view) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); - - if (tree_view->priv->selection == NULL) - tree_view->priv->selection = - _gtk_tree_selection_new_with_tree_view (tree_view); - - return tree_view->priv->selection; -} - -/** - * gtk_tree_view_get_hadjustment: - * @tree_view: A #GtkTreeView - * - * Gets the #GtkAdjustment currently being used for the horizontal aspect. - * - * Return value: A #GtkAdjustment object, or NULL if none is currently being - * used. - **/ -GtkAdjustment * -gtk_tree_view_get_hadjustment (GtkTreeView *tree_view) +static gint +gtk_tree_view_insert_iter_height (GtkTreeView *tree_view, + GtkRBTree *tree, + GtkTreeIter *iter, + gint depth) { - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); + GtkTreeViewColumn *column; + GtkCellRenderer *cell; + GList *list; + gint max_height = 0; + gint i; + gint vertical_separator; - if (tree_view->priv->hadjustment == NULL) - gtk_tree_view_set_hadjustment (tree_view, NULL); + i = 0; - return tree_view->priv->hadjustment; -} + gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical_separator", &vertical_separator, NULL); + /* do stuff with node */ + for (list = tree_view->priv->columns; list; list = list->next) + { + gint height = 0, width = 0; + column = list->data; -/** - * gtk_tree_view_set_hadjustment: - * @tree_view: A #GtkTreeView - * @adjustment: The #GtkAdjustment to set, or NULL - * - * Sets the #GtkAdjustment for the current horizontal aspect. - **/ -void -gtk_tree_view_set_hadjustment (GtkTreeView *tree_view, - GtkAdjustment *adjustment) -{ - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + if (!column->visible) + continue; - gtk_tree_view_set_adjustments (tree_view, - adjustment, - tree_view->priv->vadjustment); + if (column->column_type == GTK_TREE_VIEW_COLUMN_FIXED) + { + ++i; + continue; + } - g_object_notify (G_OBJECT (tree_view), "hadjustment"); -} + cell = column->cell; + gtk_tree_view_column_set_cell_data (column, tree_view->priv->model, iter); -/** - * gtk_tree_view_get_vadjustment: - * @tree_view: A #GtkTreeView - * - * Gets the #GtkAdjustment currently being used for the vertical aspect. - * - * Return value: A #GtkAdjustment object, or NULL if none is currently being - * used. - **/ -GtkAdjustment * -gtk_tree_view_get_vadjustment (GtkTreeView *tree_view) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); + gtk_cell_renderer_get_size (cell, GTK_WIDGET (tree_view), NULL, NULL, NULL, &width, &height); + max_height = MAX (max_height, vertical_separator + height); - if (tree_view->priv->vadjustment == NULL) - gtk_tree_view_set_vadjustment (tree_view, NULL); + if (i == tree_view->priv->expander_column && + TREE_VIEW_DRAW_EXPANDERS (tree_view)) + gtk_tree_view_column_set_width (column, + MAX (column->width, depth * tree_view->priv->tab_offset + width)); + else + gtk_tree_view_column_set_width (column, + MAX (column->width, width)); - return tree_view->priv->vadjustment; + ++i; + } + return max_height; } -/** - * gtk_tree_view_set_vadjustment: - * @tree_view: A #GtkTreeView - * @adjustment: The #GtkAdjustment to set, or NULL - * - * Sets the #GtkAdjustment for the current vertical aspect. - **/ -void -gtk_tree_view_set_vadjustment (GtkTreeView *tree_view, - GtkAdjustment *adjustment) +static void +gtk_tree_view_build_tree (GtkTreeView *tree_view, + GtkRBTree *tree, + GtkTreeIter *iter, + gint depth, + gboolean recurse, + gboolean calc_bounds) { - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + GtkRBNode *temp = NULL; + gint max_height; - gtk_tree_view_set_adjustments (tree_view, - tree_view->priv->hadjustment, - adjustment); + do + { + max_height = 0; + if (calc_bounds) + max_height = gtk_tree_view_insert_iter_height (tree_view, + tree, + iter, + depth); - g_object_notify (G_OBJECT (tree_view), "vadjustment"); + gtk_tree_model_ref_node (tree_view->priv->model, iter); + temp = _gtk_rbtree_insert_after (tree, temp, max_height); + if (recurse) + { + GtkTreeIter child; + + if (gtk_tree_model_iter_children (tree_view->priv->model, &child, iter)) + { + temp->children = _gtk_rbtree_new (); + temp->children->parent_tree = tree; + temp->children->parent_node = temp; + gtk_tree_view_build_tree (tree_view, temp->children, &child, depth + 1, recurse, calc_bounds); + } + } + if (gtk_tree_model_iter_has_child (tree_view->priv->model, iter)) + { + if ((temp->flags>K_RBNODE_IS_PARENT) != GTK_RBNODE_IS_PARENT) + temp->flags ^= GTK_RBNODE_IS_PARENT; + GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_IS_LIST); + } + } + while (gtk_tree_model_iter_next (tree_view->priv->model, iter)); } -/** - * gtk_tree_view_set_adjustments: - * @tree_view: A #GtkTreeView - * @hadj: The horizontal #GtkAdjustment to set, or NULL - * @vadj: The vertical #GtkAdjustment to set, or NULL - * - * Sets the horizonal and or vertical #GtkAdjustment. - **/ static void -gtk_tree_view_set_adjustments (GtkTreeView *tree_view, - GtkAdjustment *hadj, - GtkAdjustment *vadj) +gtk_tree_view_calc_size (GtkTreeView *tree_view, + GtkRBTree *tree, + GtkTreeIter *iter, + gint depth) { - gboolean need_adjust = FALSE; + GtkRBNode *temp; + GtkTreeIter child; + GtkCellRenderer *cell; + GList *list; + GtkTreeViewColumn *column; + gint max_height; + gint vertical_separator; + gint i; - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + TREE_VIEW_INTERNAL_ASSERT_VOID (tree != NULL); - if (hadj) - g_return_if_fail (GTK_IS_ADJUSTMENT (hadj)); - else - hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0)); - if (vadj) - g_return_if_fail (GTK_IS_ADJUSTMENT (vadj)); - else - vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0)); + gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical_separator", &vertical_separator, NULL); - if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj)) - { - gtk_signal_disconnect_by_data (GTK_OBJECT (tree_view->priv->hadjustment), tree_view); - gtk_object_unref (GTK_OBJECT (tree_view->priv->hadjustment)); - } + temp = tree->root; + while (temp->left != tree->nil) + temp = temp->left; - if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj)) + do { - gtk_signal_disconnect_by_data (GTK_OBJECT (tree_view->priv->vadjustment), tree_view); - gtk_object_unref (GTK_OBJECT (tree_view->priv->vadjustment)); - } + max_height = 0; + /* Do stuff with node */ + for (list = tree_view->priv->columns, i = 0; i < tree_view->priv->n_columns; list = list->next, i++) + { + gint height = 0, width = 0; + column = list->data; - if (tree_view->priv->hadjustment != hadj) - { - tree_view->priv->hadjustment = hadj; - gtk_object_ref (GTK_OBJECT (tree_view->priv->hadjustment)); - gtk_object_sink (GTK_OBJECT (tree_view->priv->hadjustment)); + if (!column->visible) + continue; - gtk_signal_connect (GTK_OBJECT (tree_view->priv->hadjustment), "value_changed", - (GtkSignalFunc) gtk_tree_view_adjustment_changed, - tree_view); - need_adjust = TRUE; + gtk_tree_view_column_set_cell_data (column, tree_view->priv->model, iter); + cell = column->cell; + gtk_cell_renderer_get_size (cell, GTK_WIDGET (tree_view), NULL, NULL, NULL, &width, &height); + max_height = MAX (max_height, vertical_separator + height); + + /* FIXME: I'm getting the width of all nodes here. )-: */ + if (column->dirty == FALSE) + continue; + + if (column->column_type == GTK_TREE_VIEW_COLUMN_FIXED) + { + continue; + } + if (i == tree_view->priv->expander_column && + TREE_VIEW_DRAW_EXPANDERS (tree_view)) + gtk_tree_view_column_set_width (column, + MAX (column->width, depth * tree_view->priv->tab_offset + width)); + else + gtk_tree_view_column_set_width (column, MAX (column->width, width)); + } + + _gtk_rbtree_node_set_height (tree, temp, max_height); + + if (temp->children != NULL && + gtk_tree_model_iter_children (tree_view->priv->model, &child, iter)) + gtk_tree_view_calc_size (tree_view, temp->children, &child, depth + 1); + temp = _gtk_rbtree_next (tree, temp); } + while (gtk_tree_model_iter_next (tree_view->priv->model, iter)); +} - if (tree_view->priv->vadjustment != vadj) +static gboolean +gtk_tree_view_discover_dirty_iter (GtkTreeView *tree_view, + GtkTreeIter *iter, + gint depth, + gint *height) +{ + GtkCellRenderer *cell; + GtkTreeViewColumn *column; + GList *list; + gint i; + gboolean retval = FALSE; + gint tmpheight; + + if (height) + *height = 0; + + for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++) { - tree_view->priv->vadjustment = vadj; - gtk_object_ref (GTK_OBJECT (tree_view->priv->vadjustment)); - gtk_object_sink (GTK_OBJECT (tree_view->priv->vadjustment)); + gint width; + column = list->data; + if (column->dirty == TRUE || column->column_type == GTK_TREE_VIEW_COLUMN_FIXED) + continue; + if (!column->visible) + continue; - gtk_signal_connect (GTK_OBJECT (tree_view->priv->vadjustment), "value_changed", - (GtkSignalFunc) gtk_tree_view_adjustment_changed, - tree_view); - need_adjust = TRUE; + cell = column->cell; + gtk_tree_view_column_set_cell_data (column, tree_view->priv->model, iter); + + if (height) + { + gtk_cell_renderer_get_size (cell, GTK_WIDGET (tree_view), NULL, NULL, NULL, &width, &tmpheight); + *height = MAX (*height, tmpheight); + } + else + { + gtk_cell_renderer_get_size (cell, GTK_WIDGET (tree_view), NULL, NULL, NULL, &width, NULL); + } + if (i == tree_view->priv->expander_column && + TREE_VIEW_DRAW_EXPANDERS (tree_view)) + { + if (depth * tree_view->priv->tab_offset + width > column->width) + { + column->dirty = TRUE; + retval = TRUE; + } + } + else + { + if (width > column->width) + { + column->dirty = TRUE; + retval = TRUE; + } + } } - if (need_adjust) - gtk_tree_view_adjustment_changed (NULL, tree_view); -} - - -/* Column and header operations */ - -/** - * gtk_tree_view_get_headers_visible: - * @tree_view: A #GtkTreeView. - * - * Returns TRUE if the headers on the @tree_view are visible. - * - * Return value: Whether the headers are visible or not. - **/ -gboolean -gtk_tree_view_get_headers_visible (GtkTreeView *tree_view) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); - - return GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE); + return retval; } -/** - * gtk_tree_view_set_headers_visible: - * @tree_view: A #GtkTreeView. - * @headers_visible: TRUE if the headers are visible - * - * Sets the the visibility state of the headers. - **/ -void -gtk_tree_view_set_headers_visible (GtkTreeView *tree_view, - gboolean headers_visible) +static void +gtk_tree_view_discover_dirty (GtkTreeView *tree_view, + GtkRBTree *tree, + GtkTreeIter *iter, + gint depth) { - gint x, y; - GList *list; + GtkRBNode *temp = tree->root; GtkTreeViewColumn *column; + GList *list; + GtkTreeIter child; + gboolean is_all_dirty; - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - - headers_visible = !! headers_visible; - - if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE) == headers_visible) - return; + TREE_VIEW_INTERNAL_ASSERT_VOID (tree != NULL); - if (headers_visible) - GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE); - else - GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE); + while (temp->left != tree->nil) + temp = temp->left; - if (GTK_WIDGET_REALIZED (tree_view)) + do { - gdk_window_get_position (tree_view->priv->bin_window, &x, &y); - if (headers_visible) - { - gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view)); - - if (GTK_WIDGET_MAPPED (tree_view)) - gtk_tree_view_map_buttons (tree_view); - } - else + is_all_dirty = TRUE; + for (list = tree_view->priv->columns; list; list = list->next) { - gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height); - - for (list = tree_view->priv->columns; list; list = list->next) + column = list->data; + if (column->dirty == FALSE) { - column = list->data; - gtk_widget_unmap (column->button); + is_all_dirty = FALSE; + break; } - gdk_window_hide (tree_view->priv->header_window); } - } - - tree_view->priv->vadjustment->page_size = GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view); - tree_view->priv->vadjustment->page_increment = (GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2; - tree_view->priv->vadjustment->lower = 0; - tree_view->priv->vadjustment->upper = tree_view->priv->height; - gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->vadjustment), "changed"); - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); + if (is_all_dirty) + return; - g_object_notify (G_OBJECT (tree_view), "headers_visible"); + gtk_tree_view_discover_dirty_iter (tree_view, + iter, + depth, + FALSE); + if (gtk_tree_model_iter_children (tree_view->priv->model, &child, iter) && + temp->children != NULL) + gtk_tree_view_discover_dirty (tree_view, temp->children, &child, depth + 1); + temp = _gtk_rbtree_next (tree, temp); + } + while (gtk_tree_model_iter_next (tree_view->priv->model, iter)); } -/** - * gtk_tree_view_columns_autosize: - * @tree_view: A #GtkTreeView. - * - * Resizes all columns to their optimal width. - **/ -void -gtk_tree_view_columns_autosize (GtkTreeView *tree_view) +static void +gtk_tree_view_check_dirty (GtkTreeView *tree_view) { + GtkTreePath *path; gboolean dirty = FALSE; GList *list; GtkTreeViewColumn *column; + GtkTreeIter iter; - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + if (!GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_MODEL_SETUP) && + tree_view->priv->model) + gtk_tree_view_setup_model (tree_view); for (list = tree_view->priv->columns; list; list = list->next) { column = list->data; - if (column->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE) - continue; - column->dirty = TRUE; - dirty = TRUE; + if (column->dirty) + { + dirty = TRUE; + if (column->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE) + { + gint w = 1; + + if (column->button) + w = MAX (w, column->button->requisition.width); + + gtk_tree_view_column_set_width (column, w); + } + } } - if (dirty) - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); -} + if (dirty == FALSE) + return; -/** - * gtk_tree_view_set_headers_clickable: - * @tree_view: A #GtkTreeView. - * @setting: TRUE if the columns are clickable. - * - * Allow the column title buttons to be clicked. - **/ -void -gtk_tree_view_set_headers_clickable (GtkTreeView *tree_view, - gboolean setting) -{ - GList *list; + if (tree_view->priv->model == NULL) + return; - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (tree_view->priv->model != NULL); + path = gtk_tree_path_new_root (); + if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, path)) + { + gtk_tree_view_calc_size (tree_view, tree_view->priv->tree, &iter, 1); + _gtk_tree_view_update_size (tree_view); + } - for (list = tree_view->priv->columns; list; list = list->next) - gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (list->data), setting); + gtk_tree_path_free (path); - g_object_notify (G_OBJECT (tree_view), "headers_clickable"); + for (list = tree_view->priv->columns; list; list = list->next) + { + column = list->data; + column->dirty = FALSE; + } } -/** - * gtk_tree_view_append_column: - * @tree_view: A #GtkTreeView. - * @column: The #GtkTreeViewColumn to add. - * - * Appends @column to the list of columns. - * - * Return value: The number of columns in @tree_view after appending. - **/ -gint -gtk_tree_view_append_column (GtkTreeView *tree_view, - GtkTreeViewColumn *column) +/* Make sure the node is visible vertically */ +static void +gtk_tree_view_clamp_node_visible (GtkTreeView *tree_view, + GtkRBTree *tree, + GtkRBNode *node) { - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1); - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1); - g_return_val_if_fail (column->tree_view == NULL, -1); + gint offset; - return gtk_tree_view_insert_column (tree_view, column, -1); -} + offset = _gtk_rbtree_node_find_offset (tree, node); + /* we reverse the order, b/c in the unusual case of the + * node's height being taller then the visible area, we'd rather + * have the node flush to the top + */ + if (offset + GTK_RBNODE_GET_HEIGHT (node) > + tree_view->priv->vadjustment->value + tree_view->priv->vadjustment->page_size) + gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), + offset + GTK_RBNODE_GET_HEIGHT (node) - + tree_view->priv->vadjustment->page_size); + if (offset < tree_view->priv->vadjustment->value) + gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), + offset); +} -/** - * gtk_tree_view_remove_column: - * @tree_view: A #GtkTreeView. - * @column: The #GtkTreeViewColumn to remove. - * - * Removes @column from @tree_view. - * - * Return value: The number of columns in @tree_view after removing. - **/ -gint -gtk_tree_view_remove_column (GtkTreeView *tree_view, - GtkTreeViewColumn *column) +/* This function could be more efficient. + * I'll optimize it if profiling seems to imply that + * it's important + */ +GtkTreePath * +_gtk_tree_view_find_path (GtkTreeView *tree_view, + GtkRBTree *tree, + GtkRBNode *node) { - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1); - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1); - g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1); - - _gtk_tree_view_column_unset_tree_view (column); - - if (tree_view->priv->focus_column == column) - tree_view->priv->focus_column = NULL; + GtkTreePath *path; + GtkRBTree *tmp_tree; + GtkRBNode *tmp_node, *last; + gint count; - tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column); + path = gtk_tree_path_new (); - g_object_unref (G_OBJECT (column)); + g_return_val_if_fail (node != NULL, path); + g_return_val_if_fail (node != tree->nil, path); - tree_view->priv->n_columns--; + count = 1 + node->left->count; - if (GTK_WIDGET_REALIZED (tree_view)) + last = node; + tmp_node = node->parent; + tmp_tree = tree; + while (tmp_tree) { - GList *list; - - for (list = tree_view->priv->columns; list; list = list->next) + while (tmp_node != tmp_tree->nil) { - column = GTK_TREE_VIEW_COLUMN (list->data); - if (column->visible) - column->dirty = TRUE; + if (tmp_node->right == last) + count += 1 + tmp_node->left->count; + last = tmp_node; + tmp_node = tmp_node->parent; + } + gtk_tree_path_prepend_index (path, count - 1); + last = tmp_tree->parent_node; + tmp_tree = tmp_tree->parent_tree; + if (last) + { + count = 1 + last->left->count; + tmp_node = last->parent; } - - if (tree_view->priv->n_columns == 0 && - gtk_tree_view_get_headers_visible (tree_view)) - gdk_window_hide (tree_view->priv->header_window); - - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); } - - - return tree_view->priv->n_columns; + return path; } -/** - * gtk_tree_view_insert_column: - * @tree_view: A #GtkTreeView. - * @column: The #GtkTreeViewColumn to be inserted. - * @position: The position to insert @column in. - * - * This inserts the @column into the @tree_view at @position. If @position is - * -1, then the column is inserted at the end. - * - * Return value: The number of columns in @tree_view after insertion. - **/ -gint -gtk_tree_view_insert_column (GtkTreeView *tree_view, - GtkTreeViewColumn *column, - gint position) +/* Returns TRUE if we ran out of tree before finding the path. + */ +gboolean +_gtk_tree_view_find_node (GtkTreeView *tree_view, + GtkTreePath *path, + GtkRBTree **tree, + GtkRBNode **node) { - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1); - g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1); - g_return_val_if_fail (column->tree_view == NULL, -1); - - g_object_ref (G_OBJECT (column)); - - if (tree_view->priv->n_columns == 0 && - GTK_WIDGET_REALIZED (tree_view) && - gtk_tree_view_get_headers_visible (tree_view)) - { - gdk_window_show (tree_view->priv->header_window); - } - - tree_view->priv->columns = g_list_insert (tree_view->priv->columns, - column, position); - _gtk_tree_view_column_set_tree_view (column, tree_view); - _gtk_tree_view_column_create_button (column); - - tree_view->priv->n_columns++; + GtkRBNode *tmpnode = NULL; + GtkRBTree *tmptree = tree_view->priv->tree; + gint *indices = gtk_tree_path_get_indices (path); + gint depth = gtk_tree_path_get_depth (path); + gint i = 0; + *node = NULL; + *tree = NULL; - if (GTK_WIDGET_REALIZED (tree_view)) + if (depth == 0) + return FALSE; + do { - GList *list; - - for (list = tree_view->priv->columns; list; list = list->next) + if (tmptree == NULL) { - column = GTK_TREE_VIEW_COLUMN (list->data); - if (column->visible) - column->dirty = TRUE; + *node = tmpnode; + *tree = tmptree; + return TRUE; } - gtk_widget_queue_resize (GTK_WIDGET (tree_view)); + tmpnode = _gtk_rbtree_find_count (tmptree, indices[i] + 1); + ++i; + if (i >= depth) + { + *node = tmpnode; + *tree = tmptree; + return FALSE; + } + tmptree = tmpnode->children; } - - return tree_view->priv->n_columns; + while (1); } -/** - * gtk_tree_view_insert_column_with_attributes: - * @tree_view: A #GtkTreeView - * @position: The position to insert the new column in. - * @title: The title to set the header to. - * @cell: The #GtkCellRenderer. - * @Varargs: A NULL terminated list of attributes. - * - * Creates a new #GtkTreeViewColumn and inserts it into the @tree_view at - * @position. If @position is -1, then the newly created column is inserted at - * the end. The column is initialized with the attributes given. - * - * Return value: The number of columns in @tree_view after insertion. - **/ -gint -gtk_tree_view_insert_column_with_attributes (GtkTreeView *tree_view, - gint position, - gchar *title, - GtkCellRenderer *cell, - ...) +static void +gtk_tree_view_unref_tree_helper (GtkTreeModel *model, + GtkTreeIter *iter, + GtkRBTree *tree, + GtkRBNode *node) { - GtkTreeViewColumn *column; - gchar *attribute; - va_list args; - gint column_id; - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1); + do + { + g_return_if_fail (node != NULL); - column = gtk_tree_view_column_new (); + if (node->children) + { + GtkTreeIter child; + GtkRBTree *new_tree; + GtkRBNode *new_node; - gtk_tree_view_column_set_title (column, title); - gtk_tree_view_column_set_cell_renderer (column, cell); + new_tree = node->children; + new_node = new_tree->root; - va_start (args, cell); + while (new_node && new_node->left != new_tree->nil) + new_node = new_node->left; - attribute = va_arg (args, gchar *); + g_return_if_fail (gtk_tree_model_iter_children (model, &child, iter)); + gtk_tree_view_unref_tree_helper (model, &child, new_tree, new_node); + } - while (attribute != NULL) - { - column_id = va_arg (args, gint); - gtk_tree_view_column_add_attribute (column, attribute, column_id); - attribute = va_arg (args, gchar *); + gtk_tree_model_unref_node (model, iter); + node = _gtk_rbtree_next (tree, node); } + while (gtk_tree_model_iter_next (model, iter)); +} - va_end (args); +static void +gtk_tree_view_unref_tree (GtkTreeView *tree_view, + GtkRBTree *tree) +{ + GtkTreeIter iter; + GtkTreePath *path; + GtkRBNode *node; - gtk_tree_view_insert_column (tree_view, column, position); - g_object_unref (column); + node = tree->root; + while (node && node->left != tree->nil) + node = node->left; - return tree_view->priv->n_columns; + g_return_if_fail (node != NULL); + path = _gtk_tree_view_find_path (tree_view, tree, node); + gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_view->priv->model), + &iter, path); + gtk_tree_view_unref_tree_helper (GTK_TREE_MODEL (tree_view->priv->model), &iter, tree, node); + gtk_tree_path_free (path); } -/** - * gtk_tree_view_get_column: - * @tree_view: A #GtkTreeView. - * @n: The position of the column, counting from 0. - * - * Gets the #GtkTreeViewColumn at the given position in the #tree_view. - * - * Return value: The #GtkTreeViewColumn, or NULL if the position is outside the - * range of columns. - **/ -GtkTreeViewColumn * -gtk_tree_view_get_column (GtkTreeView *tree_view, - gint n) +static void +gtk_tree_view_set_column_drag_info (GtkTreeView *tree_view, + GtkTreeViewColumn *column) { - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); - g_return_val_if_fail (tree_view->priv->model != NULL, NULL); - - if (n < 0 || n >= tree_view->priv->n_columns) - return NULL; + GtkTreeViewColumn *left_column; + GtkTreeViewColumn *cur_column; + GtkTreeViewColumnReorder *reorder; - if (tree_view->priv->columns == NULL) - return NULL; + GList *tmp_list; + gint left; - return GTK_TREE_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data); -} + /* We want to precalculate the motion list such that we know what column slots + * are available. + */ + left_column = NULL; -void -gtk_tree_view_set_expander_column (GtkTreeView *tree_view, - gint col) -{ - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + /* First, identify all possible drop spots */ + tmp_list = tree_view->priv->columns; - if (tree_view->priv->expander_column != col) + while (tmp_list) { - tree_view->priv->expander_column = col; + g_assert (tmp_list); - g_object_notify (G_OBJECT (tree_view), "expander_column"); - } -} + cur_column = GTK_TREE_VIEW_COLUMN (tmp_list->data); + tmp_list = tmp_list->next; -/** - * gtk_tree_view_get_expander_column: - * @tree_view: - * - * Returns the offset of the column that is the current expander column. This - * column has the expander arrow drawn next to it. - * - * Return value: The offset of the expander column. - **/ -gint -gtk_tree_view_get_expander_column (GtkTreeView *tree_view) -{ - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1); + if (cur_column->visible == FALSE) + continue; + + reorder = g_new (GtkTreeViewColumnReorder, 1); + reorder->left_column = left_column; + left_column = reorder->right_column = cur_column; - return tree_view->priv->expander_column; -} + tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder); + } -/** - * gtk_tree_view_scroll_to_point: - * @tree_view: a #GtkTreeView - * @tree_x: X coordinate of new top-left pixel of visible area - * @tree_y: Y coordinate of new top-left pixel of visible area - * - * Scrolls the tree view such that the top-left corner of the visible - * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified - * in tree window coordinates. The @tree_view must be realized before - * this function is called. If it isn't, you probably want ot be - * using gtk_tree_view_scroll_to_cell. - **/ -void -gtk_tree_view_scroll_to_point (GtkTreeView *tree_view, - gint tree_x, - gint tree_y) -{ - GtkAdjustment *hadj; - GtkAdjustment *vadj; + /* Add the last one */ + reorder = g_new (GtkTreeViewColumnReorder, 1); + reorder->left_column = left_column; + reorder->right_column = NULL; + tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder); - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (GTK_WIDGET_REALIZED (tree_view)); + /* Now we want to fill in the ranges for the columns, now that we've isolated them */ + left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view); - hadj = tree_view->priv->hadjustment; - vadj = tree_view->priv->vadjustment; + for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next) + { + reorder = (GtkTreeViewColumnReorder *) tmp_list->data; - gtk_adjustment_set_value (hadj, CLAMP (tree_x, hadj->lower, hadj->upper)); - gtk_adjustment_set_value (vadj, CLAMP (tree_y, vadj->lower, vadj->upper)); + reorder->left_align = left; + if (tmp_list->next != NULL) + { + g_assert (tmp_list->next->data); + left = reorder->right_align = (reorder->right_column->button->allocation.x + + reorder->right_column->button->allocation.width + + ((GtkTreeViewColumnReorder *)tmp_list->next->data)->left_column->button->allocation.x)/2; + } + else + { + gint width; + + gdk_window_get_size (tree_view->priv->header_window, &width, NULL); + reorder->right_align = width + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view); + } + } } -/** - * gtk_tree_view_scroll_to_cell - * @tree_view: A #GtkTreeView. - * @path: The path of the row to move to. - * @column: The #GtkTreeViewColumn to move horizontally to. - * @row_align: The vertical alignment of the row specified by @path. - * @col_align: The horizontal alignment of the column specified by @column. - * - * Moves the alignments of @tree_view to the position specified by - * @column and @path. If @column is NULL, then no horizontal - * scrolling occurs. Likewise, if @path is NULL no vertical scrolling - * occurs. @row_align determines where the row is placed, and - * @col_align determines where @column is placed. Both are expected - * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means - * right/bottom alignment, 0.5 means center. - **/ void -gtk_tree_view_scroll_to_cell (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column, - gfloat row_align, - gfloat col_align) +_gtk_tree_view_column_start_drag (GtkTreeView *tree_view, + GtkTreeViewColumn *column) { - GdkRectangle cell_rect; - GdkRectangle vis_rect; - gint dest_x, dest_y; + GdkEvent send_event; + GtkAllocation allocation; + gint x, y, width, height; - /* FIXME work on unmapped/unrealized trees? maybe implement when - * we do incremental reflow for trees - */ + g_return_if_fail (tree_view->priv->column_drag_info == NULL); - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (row_align >= 0.0); - g_return_if_fail (row_align <= 1.0); - g_return_if_fail (col_align >= 0.0); - g_return_if_fail (col_align <= 1.0); - g_return_if_fail (path != NULL || column != NULL); + gtk_tree_view_set_column_drag_info (tree_view, column); - row_align = CLAMP (row_align, 0.0, 1.0); - col_align = CLAMP (col_align, 0.0, 1.0); + if (tree_view->priv->column_drag_info == NULL) + return; - if (! GTK_WIDGET_REALIZED (tree_view)) + if (tree_view->priv->drag_window == NULL) { - if (path) - tree_view->priv->scroll_to_path = gtk_tree_path_copy (path); - if (column) - tree_view->priv->scroll_to_column = column; - tree_view->priv->scroll_to_row_align = row_align; - tree_view->priv->scroll_to_col_align = col_align; + GdkWindowAttr attributes; + guint attributes_mask; - return; - } + attributes.window_type = GDK_WINDOW_CHILD; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view)); + attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view)); + attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK; + attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; - gtk_tree_view_get_cell_area (tree_view, path, column, &cell_rect); - gtk_tree_view_get_visible_rect (tree_view, &vis_rect); + tree_view->priv->drag_window = gdk_window_new (tree_view->priv->bin_window, + &attributes, + attributes_mask); + gdk_window_set_user_data (tree_view->priv->drag_window, GTK_WIDGET (tree_view)); - dest_x = vis_rect.x; - dest_y = vis_rect.y; + attributes.window_type = GDK_WINDOW_TEMP; + tree_view->priv->drag_highlight_window = gdk_window_new (NULL, &attributes, attributes_mask); + gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view)); - if (path) - { - dest_x = cell_rect.x + - cell_rect.width * row_align - - vis_rect.width * row_align; + tree_view->priv->drag_header_window = gdk_window_new (NULL, &attributes, attributes_mask); + gdk_window_set_user_data (tree_view->priv->drag_header_window, GTK_WIDGET (tree_view)); } - if (column) - { - dest_y = cell_rect.y + - cell_rect.height * col_align - - vis_rect.height * col_align; - } + gdk_pointer_ungrab (GDK_CURRENT_TIME); + gdk_keyboard_ungrab (GDK_CURRENT_TIME); - gtk_tree_view_scroll_to_point (tree_view, dest_x, dest_y); -} + gtk_grab_remove (column->button); -/** - * gtk_tree_view_get_path_at_pos: - * @tree_view: A #GtkTreeView. - * @window: The #GdkWindow to check against. - * @x: The x position to be identified. - * @y: The y position to be identified. - * @path: A pointer to a #GtkTreePath pointer to be filled in, or %NULL - * @column: A pointer to a #GtkTreeViewColumn pointer to be filled in, or %NULL - * @cell_x: A pointer where the X coordinate relative to the cell can be placed, or %NULL - * @cell_y: A pointer where the Y coordinate relative to the cell can be placed, or %NULL - * - * Finds the path at the point (@x, @y) relative to @window. If - * @window is NULL, then the point is found relative to the widget - * coordinates. This function is expected to be called after an - * event, with event->window being passed in as @window. It is - * primarily for things like popup menus. If @path is non-NULL, then - * it will be filled with the #GtkTreePath at that point. This path - * should be freed with #gtk_tree_path_free. If @column is non-NULL, - * then it will be filled with the column at that point. @cell_x and - * @cell_y return the coordinates relative to the cell background - * (i.e. the background_area passed to gtk_cell_renderer_render()). - * - * Return value: TRUE if a row exists at that coordinate. - **/ -gboolean -gtk_tree_view_get_path_at_pos (GtkTreeView *tree_view, - GdkWindow *window, - gint x, - gint y, - GtkTreePath **path, - GtkTreeViewColumn **column, - gint *cell_x, - gint *cell_y) -{ - GtkRBTree *tree; - GtkRBNode *node; - gint y_offset; + send_event.crossing.type = GDK_LEAVE_NOTIFY; + send_event.crossing.send_event = TRUE; + send_event.crossing.window = column->button->window; + send_event.crossing.subwindow = NULL; + send_event.crossing.detail = GDK_NOTIFY_ANCESTOR; + send_event.crossing.time = GDK_CURRENT_TIME; - g_return_val_if_fail (tree_view != NULL, FALSE); - g_return_val_if_fail (tree_view->priv->tree != NULL, FALSE); - g_return_val_if_fail (tree_view->priv->bin_window != NULL, FALSE); + gtk_propagate_event (column->button, &send_event); - if (window) - g_return_val_if_fail (window == tree_view->priv->bin_window, FALSE); + send_event.button.type = GDK_BUTTON_RELEASE; + send_event.button.window = GDK_ROOT_PARENT (); + send_event.button.send_event = TRUE; + send_event.button.time = GDK_CURRENT_TIME; + send_event.button.x = -1; + send_event.button.y = -1; + send_event.button.axes = NULL; + send_event.button.state = 0; + send_event.button.button = 1; + send_event.button.device = gdk_core_pointer; + send_event.button.x_root = 0; + send_event.button.y_root = 0; - if (path) - *path = NULL; - if (column) - *column = NULL; + gtk_propagate_event (column->button, &send_event); - if (x > tree_view->priv->hadjustment->upper) - return FALSE; + gdk_window_move_resize (tree_view->priv->drag_window, + column->button->allocation.x, + column->button->allocation.y + column->button->allocation.height, + column->button->allocation.width, + column->button->allocation.height); + gdk_window_reparent (column->button->window, tree_view->priv->drag_window, 0, 0); + tree_view->priv->drag_column_x = column->button->allocation.x; + allocation = column->button->allocation; + allocation.x = 0; + gtk_widget_size_allocate (column->button, &allocation); + gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window); - if (x < 0 || y < 0) - return FALSE; - if (column || cell_x) - { - GtkTreeViewColumn *tmp_column; - GtkTreeViewColumn *last_column = NULL; - GList *list; - gint remaining_x = x; - gboolean found = FALSE; + tree_view->priv->drag_column = column; + gdk_window_show (tree_view->priv->drag_window); - for (list = tree_view->priv->columns; list; list = list->next) - { - tmp_column = list->data; + gdk_window_get_origin (tree_view->priv->header_window, &x, &y); + gdk_window_get_size (tree_view->priv->header_window, &width, &height); + gdk_window_move_resize (tree_view->priv->drag_header_window, x, y, width, height); + gdk_window_reparent (tree_view->priv->header_window, tree_view->priv->drag_header_window, 0, 0); + gdk_window_show (tree_view->priv->drag_header_window); - if (tmp_column->visible == FALSE) - continue; + while (gtk_events_pending ()) + gtk_main_iteration (); - last_column = tmp_column; - if (remaining_x <= tmp_column->width) - { - found = TRUE; + GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_IN_COLUMN_DRAG); + gdk_pointer_grab (tree_view->priv->drag_window, + FALSE, + GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK, + NULL, NULL, GDK_CURRENT_TIME); + +} + +static void +gtk_tree_view_queue_draw_node (GtkTreeView *tree_view, + GtkRBTree *tree, + GtkRBNode *node, + GdkRectangle *clip_rect) +{ + GdkRectangle rect; - if (column) - *column = tmp_column; + if (!GTK_WIDGET_REALIZED (tree_view)) + return; - if (cell_x) - *cell_x = remaining_x; + rect.x = 0; + rect.width = tree_view->priv->width; - break; - } - remaining_x -= tmp_column->width; - } + rect.y = BACKGROUND_FIRST_PIXEL (tree_view, tree, node); + rect.height = BACKGROUND_HEIGHT (node); - if (!found) - { - if (column) - *column = last_column; + if (clip_rect) + { + GdkRectangle new_rect; - if (cell_x) - *cell_x = last_column->width + remaining_x; - } - } + gdk_rectangle_intersect (clip_rect, &rect, &new_rect); - if (window) - { - y_offset = _gtk_rbtree_find_offset (tree_view->priv->tree, - TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y), - &tree, &node); + gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE); } else { - if (y < TREE_VIEW_HEADER_HEIGHT (tree_view)) - return FALSE; - - y_offset = _gtk_rbtree_find_offset (tree_view->priv->tree, - TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y + tree_view->priv->vadjustment->value), - &tree, &node); + gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE); } +} - if (tree == NULL) - return FALSE; - - if (cell_y) - *cell_y = y_offset; +static void +gtk_tree_view_queue_draw_path (GtkTreeView *tree_view, + GtkTreePath *path, + GdkRectangle *clip_rect) +{ + GtkRBTree *tree = NULL; + GtkRBNode *node = NULL; - if (path) - *path = _gtk_tree_view_find_path (tree_view, tree, node); + _gtk_tree_view_find_node (tree_view, path, &tree, &node); - return TRUE; + if (tree) + gtk_tree_view_queue_draw_node (tree_view, tree, node, clip_rect); } - +/* x and y are the mouse position + */ static void -gtk_tree_view_get_background_xrange (GtkTreeView *tree_view, - GtkRBTree *tree, - GtkTreeViewColumn *column, - gint *x1, - gint *x2) +gtk_tree_view_draw_arrow (GtkTreeView *tree_view, + GtkRBTree *tree, + GtkRBNode *node, + gint x, + gint y) { - GtkTreeViewColumn *tmp_column = NULL; - gint total_width; - GList *list; + GdkRectangle area; + GtkStateType state; + GtkWidget *widget; + gint x_offset = 0; + gint vertical_separator; + gint expander_height; - if (x1) - *x1 = 0; + gtk_widget_style_get (GTK_WIDGET (tree_view), + "vertical_separator", &vertical_separator, + "expander_height", &expander_height, + NULL); - if (x2) - *x2 = 0; + if (! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT)) + return; - total_width = 0; - for (list = tree_view->priv->columns; list; list = list->next) - { - tmp_column = list->data; + widget = GTK_WIDGET (tree_view); - if (tmp_column == column) - break; + gtk_tree_view_get_arrow_xrange (tree_view, &x_offset, NULL); - if (tmp_column->visible) - total_width += tmp_column->width; - } + area.x = x_offset; + area.y = CELL_FIRST_PIXEL (tree_view, tree, node, vertical_separator); + area.width = tree_view->priv->tab_offset - 2; + area.height = CELL_HEIGHT (node, vertical_separator); - if (tmp_column != column) + if (node == tree_view->priv->button_pressed_node) { - g_warning (G_STRLOC": passed-in column isn't in the tree"); - return; + if (x >= area.x && x <= (area.x + area.width) && + y >= area.y && y <= (area.y + area.height)) + state = GTK_STATE_ACTIVE; + else + state = GTK_STATE_NORMAL; } - - if (x1) - *x1 = total_width; - - if (x2) + else { - if (column->visible) - *x2 = total_width + column->width; + if (node == tree_view->priv->prelight_node && + GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_ARROW_PRELIT)) + state = GTK_STATE_PRELIGHT; else - *x2 = total_width; /* width of 0 */ + state = GTK_STATE_NORMAL; } + + gtk_paint_expander (widget->style, + tree_view->priv->bin_window, + state, + &area, + widget, + "treeview", + area.x, + (area.y + (area.height - expander_height) / 2 - (area.height + 1) % 2), + node->children != NULL); } + static void -gtk_tree_view_get_cell_xrange (GtkTreeView *tree_view, - GtkRBTree *tree, - GtkTreeViewColumn *column, - gint *x1, - gint *x2) +_gtk_tree_view_update_col_width (GtkTreeView *tree_view) { - GtkTreeViewColumn *tmp_column = NULL; - gint total_width; - GList *list; - gint i; + GList *list, *last_column; + GtkTreeViewColumn *column; + gint width = 0; - if (x1) - *x1 = 0; + for (last_column = g_list_last (tree_view->priv->columns); + last_column && + !(GTK_TREE_VIEW_COLUMN (last_column->data)->visible) && + GTK_WIDGET_CAN_FOCUS (GTK_TREE_VIEW_COLUMN (last_column->data)->button); + last_column = last_column->prev) + ; - if (x2) - *x2 = 0; + if (last_column == NULL) + return; - i = 0; - total_width = 0; - for (list = tree_view->priv->columns; list; list = list->next) + for (list = tree_view->priv->columns; list != last_column; list = list->next) { - tmp_column = list->data; + column = GTK_TREE_VIEW_COLUMN (list->data); + if (! column->visible) + continue; - if (tmp_column == column) - break; + width += column->width; + column->displayed_width = (CLAMP (column->width, (column->min_width!=-1)?column->min_width:column->width, (column->max_width!=-1)?column->max_width:column->width)); + } + column = GTK_TREE_VIEW_COLUMN (last_column->data); + column->displayed_width = MAX (GTK_WIDGET (tree_view)->allocation.width, tree_view->priv->width) - width; +} - if (tmp_column->visible) - total_width += tmp_column->width; +void +_gtk_tree_view_update_size (GtkTreeView *tree_view) +{ + gint width, height; + GList *list; + GtkTreeViewColumn *column; + gint vertical_separator; + gint i; - ++i; - } + gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical_separator", &vertical_separator, NULL); - if (tmp_column != column) + if (tree_view->priv->model == NULL) { - g_warning (G_STRLOC": passed-in column isn't in the tree"); + tree_view->priv->width = 0; + tree_view->priv->height = 0; + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); return; } - /* Remember we're getting the cell range, i.e. the cell_area passed - * to the cell renderer. - */ - - if (i == tree_view->priv->expander_column) - total_width += tree_view->priv->tab_offset * _gtk_rbtree_get_depth (tree); + width = 0; + for (list = tree_view->priv->columns, i = 0; list; list = list->next, i++) + { + column = list->data; + if (!column->visible) + continue; + width += TREE_VIEW_COLUMN_WIDTH (column); + } - if (x1) - *x1 = total_width; + if (tree_view->priv->tree == NULL) + height = 0; + else + height = tree_view->priv->tree->root->offset + vertical_separator; - if (x2) + if (tree_view->priv->width != width) { - if (column->visible) - *x2 = total_width + column->displayed_width; - else - *x2 = total_width; /* width of 0 */ + tree_view->priv->width = width; + tree_view->priv->hadjustment->upper = width; + gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->hadjustment), "changed"); } -} -static void -gtk_tree_view_get_arrow_xrange (GtkTreeView *tree_view, - gint *x1, - gint *x2) -{ - gint x_offset = 0; - GList *list; - GtkTreeViewColumn *tmp_column = NULL; - gint total_width; - gint i; + if (tree_view->priv->height != height) + { + tree_view->priv->height = height; + tree_view->priv->vadjustment->upper = tree_view->priv->height; + gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->vadjustment), "changed"); + } - i = 0; - total_width = 0; - for (list = tree_view->priv->columns; list; list = list->next) + if (GTK_WIDGET_REALIZED (tree_view)) { - tmp_column = list->data; + gdk_window_resize (tree_view->priv->bin_window, MAX (width, GTK_WIDGET (tree_view)->allocation.width), height + TREE_VIEW_HEADER_HEIGHT (tree_view)); + gdk_window_resize (tree_view->priv->header_window, MAX (width, GTK_WIDGET (tree_view)->allocation.width), tree_view->priv->header_height); + + _gtk_tree_view_update_col_width (tree_view); + } - if (i == tree_view->priv->expander_column) - { - x_offset = total_width; - break; - } + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); +} - if (tmp_column->visible) - total_width += tmp_column->width; +/* this function returns the new width of the column being resized given + * the column and x position of the cursor; the x cursor position is passed + * in as a pointer and automagicly corrected if it's beyond min/max limits + */ +static gint +gtk_tree_view_new_column_width (GtkTreeView *tree_view, + gint i, + gint *x) +{ + GtkTreeViewColumn *column; + gint width; - ++i; - } + /* first translate the x position from widget->window + * to clist->clist_window + */ - if (x1) - *x1 = x_offset; + column = g_list_nth (tree_view->priv->columns, i)->data; + width = *x - column->button->allocation.x; - if (tmp_column && tmp_column->visible) - { - /* +1 because x2 isn't included in the range. */ - if (x2) - *x2 = x_offset + tree_view->priv->tab_offset + 1; - } + /* Clamp down the value */ + if (column->min_width == -1) + width = MAX (column->button->requisition.width, + width); else + width = MAX (column->min_width, + width); + if (column->max_width != -1) + width = MIN (width, column->max_width != -1); + *x = column->button->allocation.x + width; + + return width; +} + +/* Callbacks */ +static void +gtk_tree_view_adjustment_changed (GtkAdjustment *adjustment, + GtkTreeView *tree_view) +{ + if (GTK_WIDGET_REALIZED (tree_view)) { - /* return an empty range, the expander column is hidden */ - if (x2) - *x2 = x_offset; + gdk_window_move (tree_view->priv->bin_window, + - tree_view->priv->hadjustment->value, + - tree_view->priv->vadjustment->value); + gdk_window_move (tree_view->priv->header_window, + - tree_view->priv->hadjustment->value, + 0); + + gdk_window_process_updates (tree_view->priv->bin_window, TRUE); + gdk_window_process_updates (tree_view->priv->header_window, TRUE); } } + + +/* Public methods + */ /** - * gtk_tree_view_get_cell_area: - * @tree_view: a #GtkTreeView - * @path: a #GtkTreePath for the row, or %NULL to get only horizontal coordinates - * @column: a #GtkTreeViewColumn for the column, or %NULL to get only vertical coordiantes - * @rect: rectangle to fill with cell rect + * gtk_tree_view_new: * - * Fills the bounding rectangle in tree window coordinates for the - * cell at the row specified by @path and the column specified by - * @column. If @path is %NULL, the y and height fields of the - * rectangle will be filled with 0. If @column is %NULL, the x and - * width fields will be filled with 0. The sum of all cell rects does - * not cover the entire tree; there are extra pixels in between rows, - * for example. The returned rectangle is equivalent to the @cell_area - * passed to gtk_cell_renderer_render(). + * Creates a new #GtkTreeView widget. * + * Return value: A newly created #GtkTreeView widget. **/ -void -gtk_tree_view_get_cell_area (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column, - GdkRectangle *rect) +GtkWidget * +gtk_tree_view_new (void) { - GtkRBTree *tree = NULL; - GtkRBNode *node = NULL; - gint vertical_separator; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column)); - g_return_if_fail (rect != NULL); - - gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical_separator", &vertical_separator, NULL); - - rect->x = 0; - rect->y = 0; - rect->width = 0; - rect->height = 0; - - if (path) - { - /* Get vertical coords */ - - _gtk_tree_view_find_node (tree_view, path, &tree, &node); + GtkTreeView *tree_view; - if (tree == NULL) - { - g_warning (G_STRLOC": no row corresponding to path"); - return; - } + tree_view = GTK_TREE_VIEW (gtk_type_new (gtk_tree_view_get_type ())); - /* Remember that the rbtree stores node height including the vertical - * separator, see comment at top of file. - */ - rect->y = CELL_FIRST_PIXEL (tree_view, tree, node, vertical_separator); + return GTK_WIDGET (tree_view); +} - rect->height = CELL_HEIGHT (node, vertical_separator); - } +/** + * gtk_tree_view_new_with_model: + * @model: the model. + * + * Creates a new #GtkTreeView widget with the model initialized to @model. + * + * Return value: A newly created #GtkTreeView widget. + **/ +GtkWidget * +gtk_tree_view_new_with_model (GtkTreeModel *model) +{ + GtkTreeView *tree_view; - if (column) - { - gint x2 = 0; + tree_view = GTK_TREE_VIEW (gtk_type_new (gtk_tree_view_get_type ())); + gtk_tree_view_set_model (tree_view, model); - gtk_tree_view_get_cell_xrange (tree_view, tree, column, &rect->x, &x2); - rect->width = x2 - rect->x; - } + return GTK_WIDGET (tree_view); } +/* Public Accessors + */ /** - * gtk_tree_view_get_background_area: + * gtk_tree_view_get_model: * @tree_view: a #GtkTreeView - * @path: a #GtkTreePath for the row, or %NULL to get only horizontal coordinates - * @column: a #GtkTreeViewColumn for the column, or %NULL to get only vertical coordiantes - * @rect: rectangle to fill with cell background rect * - * Fills the bounding rectangle in tree window coordinates for the - * cell at the row specified by @path and the column specified by - * @column. If @path is %NULL, the y and height fields of the - * rectangle will be filled with 0. If @column is %NULL, the x and - * width fields will be filled with 0. The returned rectangle is - * equivalent to the @background_area passed to - * gtk_cell_renderer_render(). These background areas tile to cover - * the entire tree window (except for the area used for header - * buttons). Contrast with the cell_area, returned by - * gtk_tree_view_get_cell_area(), which returns only the cell itself, - * excluding surrounding borders and the tree expander area. + * Returns the model the the #GtkTreeView is based on. Returns NULL if the + * model is unset. * + * Return value: A #GtkTreeModel, or NULL if none is currently being used. **/ -void -gtk_tree_view_get_background_area (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column, - GdkRectangle *rect) +GtkTreeModel * +gtk_tree_view_get_model (GtkTreeView *tree_view) { - GtkRBTree *tree = NULL; - GtkRBNode *node = NULL; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column)); - g_return_if_fail (rect != NULL); - - rect->x = 0; - rect->y = 0; - rect->width = 0; - rect->height = 0; - - if (path) - { - /* Get vertical coords */ + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); - _gtk_tree_view_find_node (tree_view, path, &tree, &node); + return tree_view->priv->model; +} - if (tree == NULL) - { - g_warning (G_STRLOC": no row corresponding to path"); - return; - } +/** + * gtk_tree_view_set_model: + * @tree_view: A #GtkTreeNode. + * @model: The model. + * + * Sets the model for a #GtkTreeView. If the @tree_view already has a model + * set, it will remove it before setting the new model. If @model is NULL, then + * it will unset the old model. + **/ +void +gtk_tree_view_set_model (GtkTreeView *tree_view, + GtkTreeModel *model) +{ + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - rect->y = BACKGROUND_FIRST_PIXEL (tree_view, tree, node); + if (model == tree_view->priv->model) + return; - rect->height = BACKGROUND_HEIGHT (node); - } + if (model != NULL) + g_object_ref (model); - if (column) + if (tree_view->priv->model != NULL) { - gint x2 = 0; - - gtk_tree_view_get_background_xrange (tree_view, tree, column, &rect->x, &x2); - rect->width = x2 - rect->x; - } -} + if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_MODEL_SETUP)) + { + g_signal_handlers_disconnect_matched (G_OBJECT (tree_view->priv->model), + G_SIGNAL_MATCH_DATA, + 0, 0, NULL, + NULL, tree_view); + g_signal_handlers_disconnect_matched (G_OBJECT (tree_view->priv->model), + G_SIGNAL_MATCH_DATA, + 0, 0, NULL, + NULL, tree_view); + g_signal_handlers_disconnect_matched (G_OBJECT (tree_view->priv->model), + G_SIGNAL_MATCH_DATA, + 0, 0, NULL, + NULL, tree_view); + g_signal_handlers_disconnect_matched (G_OBJECT (tree_view->priv->model), + G_SIGNAL_MATCH_DATA, + 0, 0, NULL, + NULL, tree_view); + g_signal_handlers_disconnect_matched (G_OBJECT (tree_view->priv->model), + G_SIGNAL_MATCH_DATA, + 0, 0, NULL, + NULL, tree_view); + _gtk_rbtree_free (tree_view->priv->tree); + } -static void -gtk_tree_view_expand_all_helper (GtkRBTree *tree, - GtkRBNode *node, - gpointer data) -{ - GtkTreeView *tree_view = data; + if (tree_view->priv->drag_dest_row) + gtk_tree_row_reference_free (tree_view->priv->drag_dest_row); - if (node->children) - _gtk_rbtree_traverse (node->children, - node->children->root, - G_PRE_ORDER, - gtk_tree_view_expand_all_helper, - data); - else if ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT && node->children == NULL) - { - GtkTreePath *path; - GtkTreeIter iter; - GtkTreeIter child; + GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_MODEL_SETUP); + g_object_unref (tree_view->priv->model); + } - node->children = _gtk_rbtree_new (); - node->children->parent_tree = tree; - node->children->parent_node = node; - path = _gtk_tree_view_find_path (tree_view, tree, node); - gtk_tree_model_get_iter (tree_view->priv->model, &iter, path); - gtk_tree_model_iter_children (tree_view->priv->model, &child, &iter); - gtk_tree_view_build_tree (tree_view, - node->children, - &child, - gtk_tree_path_get_depth (path) + 1, - TRUE, - GTK_WIDGET_REALIZED (tree_view)); - gtk_tree_path_free (path); + tree_view->priv->model = model; + + if (model == NULL) + { + tree_view->priv->tree = NULL; + if (GTK_WIDGET_REALIZED (tree_view)) + _gtk_tree_view_update_size (tree_view); + } + else if (GTK_WIDGET_REALIZED (tree_view)) + { + gtk_tree_view_setup_model (tree_view); + _gtk_tree_view_update_size (tree_view); } + + g_object_notify (G_OBJECT (tree_view), "model"); } /** - * gtk_tree_view_expand_all: + * gtk_tree_view_get_selection: * @tree_view: A #GtkTreeView. * - * Recursively expands all nodes in the @tree_view. + * Gets the #GtkTreeSelection associated with @tree_view. + * + * Return value: A #GtkTreeSelection object. **/ -void -gtk_tree_view_expand_all (GtkTreeView *tree_view) +GtkTreeSelection * +gtk_tree_view_get_selection (GtkTreeView *tree_view) { - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (tree_view->priv->tree != NULL); + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); - _gtk_rbtree_traverse (tree_view->priv->tree, - tree_view->priv->tree->root, - G_PRE_ORDER, - gtk_tree_view_expand_all_helper, - tree_view); + if (tree_view->priv->selection == NULL) + tree_view->priv->selection = _gtk_tree_selection_new_with_tree_view (tree_view); - _gtk_tree_view_update_size (tree_view); + return tree_view->priv->selection; } -static void -gtk_tree_view_collapse_all_helper (GtkRBTree *tree, - GtkRBNode *node, - gpointer data) +/** + * gtk_tree_view_get_hadjustment: + * @tree_view: A #GtkTreeView + * + * Gets the #GtkAdjustment currently being used for the horizontal aspect. + * + * Return value: A #GtkAdjustment object, or NULL if none is currently being + * used. + **/ +GtkAdjustment * +gtk_tree_view_get_hadjustment (GtkTreeView *tree_view) { - if (node->children) - { - GtkTreePath *path; - GtkTreeIter iter; - - path = _gtk_tree_view_find_path (GTK_TREE_VIEW (data), - node->children, - node->children->root); - gtk_tree_model_get_iter (GTK_TREE_VIEW (data)->priv->model, - &iter, - path); - gtk_tree_view_discover_dirty (GTK_TREE_VIEW (data), - node->children, - &iter, - gtk_tree_path_get_depth (path)); + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); - /* Ensure we don't have a dangling pointer to a dead node */ - ensure_unprelighted (GTK_TREE_VIEW (data)); + if (tree_view->priv->hadjustment == NULL) + gtk_tree_view_set_hadjustment (tree_view, NULL); - _gtk_rbtree_remove (node->children); - gtk_tree_path_free (path); - } + return tree_view->priv->hadjustment; } /** - * gtk_tree_view_collapse_all: - * @tree_view: A #GtkTreeView. + * gtk_tree_view_set_hadjustment: + * @tree_view: A #GtkTreeView + * @adjustment: The #GtkAdjustment to set, or NULL * - * Recursively collapses all visible, expanded nodes in @tree_view. + * Sets the #GtkAdjustment for the current horizontal aspect. **/ void -gtk_tree_view_collapse_all (GtkTreeView *tree_view) +gtk_tree_view_set_hadjustment (GtkTreeView *tree_view, + GtkAdjustment *adjustment) { g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (tree_view->priv->tree != NULL); - _gtk_rbtree_traverse (tree_view->priv->tree, - tree_view->priv->tree->root, - G_PRE_ORDER, - gtk_tree_view_collapse_all_helper, - tree_view); + gtk_tree_view_set_adjustments (tree_view, + adjustment, + tree_view->priv->vadjustment); - if (GTK_WIDGET_MAPPED (tree_view)) - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + g_object_notify (G_OBJECT (tree_view), "hadjustment"); } -/* FIXME the bool return values for expand_row and collapse_row are - * not analagous; they should be TRUE if the row had children and - * was not already in the requested state. - */ - /** - * gtk_tree_view_expand_row: - * @tree_view: a #GtkTreeView - * @path: path to a row - * @open_all: whether to recursively expand, or just expand immediate children + * gtk_tree_view_get_vadjustment: + * @tree_view: A #GtkTreeView * - * Opens the row so its children are visible + * Gets the #GtkAdjustment currently being used for the vertical aspect. * - * Return value: %TRUE if the row existed and had children + * Return value: A #GtkAdjustment object, or NULL if none is currently being + * used. **/ -gboolean -gtk_tree_view_expand_row (GtkTreeView *tree_view, - GtkTreePath *path, - gboolean open_all) +GtkAdjustment * +gtk_tree_view_get_vadjustment (GtkTreeView *tree_view) { - GtkTreeIter iter; - GtkTreeIter child; - GtkRBTree *tree; - GtkRBNode *node; - gboolean expand; - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); - g_return_val_if_fail (tree_view->priv->model != NULL, FALSE); - g_return_val_if_fail (path != NULL, FALSE); - - if (_gtk_tree_view_find_node (tree_view, - path, - &tree, - &node)) - return FALSE; - - if (node->children) - return TRUE; - - gtk_tree_model_get_iter (tree_view->priv->model, &iter, path); - if (! gtk_tree_model_iter_has_child (tree_view->priv->model, &iter)) - return FALSE; - - g_signal_emit (G_OBJECT (tree_view), tree_view_signals[EXPAND_ROW], 0, &iter, path, &expand); - - if (expand) - return FALSE; - - node->children = _gtk_rbtree_new (); - node->children->parent_tree = tree; - node->children->parent_node = node; - - gtk_tree_model_iter_children (tree_view->priv->model, &child, &iter); - gtk_tree_view_build_tree (tree_view, - node->children, - &child, - gtk_tree_path_get_depth (path) + 1, - open_all, - GTK_WIDGET_REALIZED (tree_view)); + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); - if (GTK_WIDGET_MAPPED (tree_view)) - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + if (tree_view->priv->vadjustment == NULL) + gtk_tree_view_set_vadjustment (tree_view, NULL); - return TRUE; + return tree_view->priv->vadjustment; } /** - * gtk_tree_view_collapse_row: - * @tree_view: a #GtkTreeView - * @path: path to a row in the @tree_view - * - * Collapses a row (hides its child rows, if they exist.) + * gtk_tree_view_set_vadjustment: + * @tree_view: A #GtkTreeView + * @adjustment: The #GtkAdjustment to set, or NULL * - * Return value: %TRUE if the row was collapsed. + * Sets the #GtkAdjustment for the current vertical aspect. **/ -gboolean -gtk_tree_view_collapse_row (GtkTreeView *tree_view, - GtkTreePath *path) +void +gtk_tree_view_set_vadjustment (GtkTreeView *tree_view, + GtkAdjustment *adjustment) { - GtkRBTree *tree; - GtkRBNode *node; - GtkTreeIter iter; - gboolean collapse; - - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); - g_return_val_if_fail (tree_view->priv->tree != NULL, FALSE); - g_return_val_if_fail (path != NULL, FALSE); - - if (_gtk_tree_view_find_node (tree_view, - path, - &tree, - &node)) - return FALSE; - - if (node->children == NULL) - return FALSE; - - gtk_tree_model_get_iter (tree_view->priv->model, &iter, path); - - g_signal_emit (G_OBJECT (tree_view), tree_view_signals[COLLAPSE_ROW], 0, &iter, path, &collapse); - - if (collapse) - return FALSE; - - gtk_tree_view_discover_dirty (tree_view, - node->children, - &iter, - gtk_tree_path_get_depth (path)); + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - /* Ensure we don't have a dangling pointer to a dead node */ - ensure_unprelighted (tree_view); + gtk_tree_view_set_adjustments (tree_view, + tree_view->priv->hadjustment, + adjustment); - g_assert (tree_view->priv->prelight_node == NULL); + g_object_notify (G_OBJECT (tree_view), "vadjustment"); +} - _gtk_rbtree_remove (node->children); +/* Column and header operations */ - if (GTK_WIDGET_MAPPED (tree_view)) - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); +/** + * gtk_tree_view_get_headers_visible: + * @tree_view: A #GtkTreeView. + * + * Returns TRUE if the headers on the @tree_view are visible. + * + * Return value: Whether the headers are visible or not. + **/ +gboolean +gtk_tree_view_get_headers_visible (GtkTreeView *tree_view) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); - return TRUE; + return GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE); } /** - * gtk_tree_view_get_visible_rect: - * @tree_view: a #GtkTreeView - * @visible_rect: rectangle to fill + * gtk_tree_view_set_headers_visible: + * @tree_view: A #GtkTreeView. + * @headers_visible: TRUE if the headers are visible * - * Fills @visible_rect with the currently-visible region of the - * buffer, in tree coordinates. Convert to widget coordinates with - * gtk_tree_view_tree_to_widget_coords(). Tree coordinates start at - * 0,0 for row 0 of the tree, and cover the entire scrollable area of - * the tree. + * Sets the the visibility state of the headers. **/ void -gtk_tree_view_get_visible_rect (GtkTreeView *tree_view, - GdkRectangle *visible_rect) +gtk_tree_view_set_headers_visible (GtkTreeView *tree_view, + gboolean headers_visible) { - GtkWidget *widget; + gint x, y; + GList *list; + GtkTreeViewColumn *column; g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - widget = GTK_WIDGET (tree_view); + headers_visible = !! headers_visible; - if (visible_rect) + if (GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE) == headers_visible) + return; + + if (headers_visible) + GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE); + else + GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_HEADERS_VISIBLE); + + if (GTK_WIDGET_REALIZED (tree_view)) { - visible_rect->x = tree_view->priv->hadjustment->value; - visible_rect->y = tree_view->priv->vadjustment->value; - visible_rect->width = widget->allocation.width; - visible_rect->height = widget->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view); + gdk_window_get_position (tree_view->priv->bin_window, &x, &y); + if (headers_visible) + { + gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view)); + + if (GTK_WIDGET_MAPPED (tree_view)) + gtk_tree_view_map_buttons (tree_view); + } + else + { + gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height); + + for (list = tree_view->priv->columns; list; list = list->next) + { + column = list->data; + gtk_widget_unmap (column->button); + } + gdk_window_hide (tree_view->priv->header_window); + } } + + tree_view->priv->vadjustment->page_size = GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view); + tree_view->priv->vadjustment->page_increment = (GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2; + tree_view->priv->vadjustment->lower = 0; + tree_view->priv->vadjustment->upper = tree_view->priv->height; + gtk_signal_emit_by_name (GTK_OBJECT (tree_view->priv->vadjustment), "changed"); + + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); + + g_object_notify (G_OBJECT (tree_view), "headers_visible"); } + /** - * gtk_tree_view_widget_to_tree_coords: - * @tree_view: a #GtkTreeView - * @wx: widget X coordinate - * @wy: widget Y coordinate - * @tx: return location for tree X coordinate - * @ty: return location for tree Y coordinate - * - * Converts widget coordinates to coordinates for the - * tree window (the full scrollable area of the tree). + * gtk_tree_view_columns_autosize: + * @tree_view: A #GtkTreeView. * + * Resizes all columns to their optimal width. **/ void -gtk_tree_view_widget_to_tree_coords (GtkTreeView *tree_view, - gint wx, - gint wy, - gint *tx, - gint *ty) +gtk_tree_view_columns_autosize (GtkTreeView *tree_view) { + gboolean dirty = FALSE; + GList *list; + GtkTreeViewColumn *column; + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - if (tx) + for (list = tree_view->priv->columns; list; list = list->next) { - *tx = wx + tree_view->priv->hadjustment->value; + column = list->data; + if (column->column_type == GTK_TREE_VIEW_COLUMN_AUTOSIZE) + continue; + column->dirty = TRUE; + dirty = TRUE; } - if (ty) - { - *ty = wy + tree_view->priv->vadjustment->value; - } + if (dirty) + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); } /** - * gtk_tree_view_tree_to_widget_coords: - * @tree_view: a #GtkTreeView - * @tx: tree X coordinate - * @ty: tree Y coordinate - * @wx: return location for widget X coordinate - * @wy: return location for widget Y coordinate - * - * Converts tree coordinates (coordinates in full scrollable - * area of the tree) to widget coordinates. + * gtk_tree_view_set_headers_clickable: + * @tree_view: A #GtkTreeView. + * @setting: TRUE if the columns are clickable. * + * Allow the column title buttons to be clicked. **/ void -gtk_tree_view_tree_to_widget_coords (GtkTreeView *tree_view, - gint tx, - gint ty, - gint *wx, - gint *wy) +gtk_tree_view_set_headers_clickable (GtkTreeView *tree_view, + gboolean setting) { + GList *list; + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (tree_view->priv->model != NULL); - if (wx) - { - *wx = tx - tree_view->priv->hadjustment->value; - } + for (list = tree_view->priv->columns; list; list = list->next) + gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (list->data), setting); - if (wy) - { - *wy = ty - tree_view->priv->vadjustment->value; - } + g_object_notify (G_OBJECT (tree_view), "headers_clickable"); } + /** * gtk_tree_view_set_rules_hint * @tree_view: a #GtkTreeView @@ -5904,1131 +5755,1398 @@ void gtk_tree_view_set_rules_hint (GtkTreeView *tree_view, gboolean setting) { - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + setting = setting != FALSE; + + if (tree_view->priv->has_rules != setting) + { + tree_view->priv->has_rules = setting; + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + } + + g_object_notify (G_OBJECT (tree_view), "rules_hint"); +} + +/** + * gtk_tree_view_get_rules_hint + * @tree_view: a #GtkTreeView + * + * Gets the setting set by gtk_tree_view_set_rules_hint(). + * + * Return value: %TRUE if rules are useful for the user of this tree + **/ +gboolean +gtk_tree_view_get_rules_hint (GtkTreeView *tree_view) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); + + return tree_view->priv->has_rules; +} + +/* Public Column functions + */ + +/** + * gtk_tree_view_append_column: + * @tree_view: A #GtkTreeView. + * @column: The #GtkTreeViewColumn to add. + * + * Appends @column to the list of columns. + * + * Return value: The number of columns in @tree_view after appending. + **/ +gint +gtk_tree_view_append_column (GtkTreeView *tree_view, + GtkTreeViewColumn *column) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1); + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1); + g_return_val_if_fail (column->tree_view == NULL, -1); + + return gtk_tree_view_insert_column (tree_view, column, -1); +} + + +/** + * gtk_tree_view_remove_column: + * @tree_view: A #GtkTreeView. + * @column: The #GtkTreeViewColumn to remove. + * + * Removes @column from @tree_view. + * + * Return value: The number of columns in @tree_view after removing. + **/ +gint +gtk_tree_view_remove_column (GtkTreeView *tree_view, + GtkTreeViewColumn *column) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1); + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1); + g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1); + + _gtk_tree_view_column_unset_tree_view (column); + + if (tree_view->priv->focus_column == column) + tree_view->priv->focus_column = NULL; + + tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column); + + g_object_unref (G_OBJECT (column)); + + tree_view->priv->n_columns--; + + if (GTK_WIDGET_REALIZED (tree_view)) + { + GList *list; + + for (list = tree_view->priv->columns; list; list = list->next) + { + column = GTK_TREE_VIEW_COLUMN (list->data); + if (column->visible) + column->dirty = TRUE; + } + + if (tree_view->priv->n_columns == 0 && + gtk_tree_view_get_headers_visible (tree_view)) + gdk_window_hide (tree_view->priv->header_window); + + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); + } + + + return tree_view->priv->n_columns; +} + +/** + * gtk_tree_view_insert_column: + * @tree_view: A #GtkTreeView. + * @column: The #GtkTreeViewColumn to be inserted. + * @position: The position to insert @column in. + * + * This inserts the @column into the @tree_view at @position. If @position is + * -1, then the column is inserted at the end. + * + * Return value: The number of columns in @tree_view after insertion. + **/ +gint +gtk_tree_view_insert_column (GtkTreeView *tree_view, + GtkTreeViewColumn *column, + gint position) +{ + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1); + g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN (column), -1); + g_return_val_if_fail (column->tree_view == NULL, -1); - setting = setting != FALSE; + g_object_ref (G_OBJECT (column)); - if (tree_view->priv->has_rules != setting) + if (tree_view->priv->n_columns == 0 && + GTK_WIDGET_REALIZED (tree_view) && + gtk_tree_view_get_headers_visible (tree_view)) { - tree_view->priv->has_rules = setting; - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + gdk_window_show (tree_view->priv->header_window); } - g_object_notify (G_OBJECT (tree_view), "rules_hint"); + tree_view->priv->columns = g_list_insert (tree_view->priv->columns, + column, position); + _gtk_tree_view_column_set_tree_view (column, tree_view); + _gtk_tree_view_column_create_button (column); + + tree_view->priv->n_columns++; + + + if (GTK_WIDGET_REALIZED (tree_view)) + { + GList *list; + + for (list = tree_view->priv->columns; list; list = list->next) + { + column = GTK_TREE_VIEW_COLUMN (list->data); + if (column->visible) + column->dirty = TRUE; + } + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); + } + + return tree_view->priv->n_columns; } /** - * gtk_tree_view_get_rules_hint - * @tree_view: a #GtkTreeView + * gtk_tree_view_insert_column_with_attributes: + * @tree_view: A #GtkTreeView + * @position: The position to insert the new column in. + * @title: The title to set the header to. + * @cell: The #GtkCellRenderer. + * @Varargs: A NULL terminated list of attributes. * - * Gets the setting set by gtk_tree_view_set_rules_hint(). + * Creates a new #GtkTreeViewColumn and inserts it into the @tree_view at + * @position. If @position is -1, then the newly created column is inserted at + * the end. The column is initialized with the attributes given. * - * Return value: %TRUE if rules are useful for the user of this tree + * Return value: The number of columns in @tree_view after insertion. **/ -gboolean -gtk_tree_view_get_rules_hint (GtkTreeView *tree_view) +gint +gtk_tree_view_insert_column_with_attributes (GtkTreeView *tree_view, + gint position, + gchar *title, + GtkCellRenderer *cell, + ...) { - g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); - - return tree_view->priv->has_rules; -} - -/* Drag-and-drop */ + GtkTreeViewColumn *column; + gchar *attribute; + va_list args; + gint column_id; -static void -set_source_row (GdkDragContext *context, - GtkTreeModel *model, - GtkTreePath *source_row) -{ - g_object_set_data_full (G_OBJECT (context), - "gtk-tree-view-source-row", - source_row ? gtk_tree_row_reference_new (model, source_row) : NULL, - (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL)); -} + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1); -static GtkTreePath* -get_source_row (GdkDragContext *context) -{ - GtkTreeRowReference *ref = - g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row"); + column = gtk_tree_view_column_new (); - if (ref) - return gtk_tree_row_reference_get_path (ref); - else - return NULL; -} + gtk_tree_view_column_set_title (column, title); + gtk_tree_view_column_set_cell_renderer (column, cell); + va_start (args, cell); -static void -set_dest_row (GdkDragContext *context, - GtkTreeModel *model, - GtkTreePath *dest_row) -{ - g_object_set_data_full (G_OBJECT (context), - "gtk-tree-view-dest-row", - dest_row ? gtk_tree_row_reference_new (model, dest_row) : NULL, - (GDestroyNotify) (dest_row ? gtk_tree_row_reference_free : NULL)); -} + attribute = va_arg (args, gchar *); -static GtkTreePath* -get_dest_row (GdkDragContext *context) -{ - GtkTreeRowReference *ref = - g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row"); + while (attribute != NULL) + { + column_id = va_arg (args, gint); + gtk_tree_view_column_add_attribute (column, attribute, column_id); + attribute = va_arg (args, gchar *); + } - if (ref) - return gtk_tree_row_reference_get_path (ref); - else - return NULL; -} + va_end (args); -/* Get/set whether drag_motion requested the drag data and - * drag_data_received should thus not actually insert the data, - * since the data doesn't result from a drop. - */ -static void -set_status_pending (GdkDragContext *context, - GdkDragAction suggested_action) -{ - g_object_set_data (G_OBJECT (context), - "gtk-tree-view-status-pending", - GINT_TO_POINTER (suggested_action)); -} + gtk_tree_view_insert_column (tree_view, column, position); + g_object_unref (column); -static GdkDragAction -get_status_pending (GdkDragContext *context) -{ - return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context), - "gtk-tree-view-status-pending")); + return tree_view->priv->n_columns; } -typedef struct _TreeViewDragInfo TreeViewDragInfo; - -struct _TreeViewDragInfo +/** + * gtk_tree_view_get_column: + * @tree_view: A #GtkTreeView. + * @n: The position of the column, counting from 0. + * + * Gets the #GtkTreeViewColumn at the given position in the #tree_view. + * + * Return value: The #GtkTreeViewColumn, or NULL if the position is outside the + * range of columns. + **/ +GtkTreeViewColumn * +gtk_tree_view_get_column (GtkTreeView *tree_view, + gint n) { - GdkModifierType start_button_mask; - GtkTargetList *source_target_list; - GdkDragAction source_actions; - GClosure *row_draggable_closure; + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL); + g_return_val_if_fail (tree_view->priv->model != NULL, NULL); - GtkTargetList *dest_target_list; - GClosure *location_droppable_closure; + if (n < 0 || n >= tree_view->priv->n_columns) + return NULL; - guint source_set : 1; - guint dest_set : 1; -}; + if (tree_view->priv->columns == NULL) + return NULL; -static TreeViewDragInfo* -get_info (GtkTreeView *tree_view) -{ - return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info"); + return GTK_TREE_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data); } -static void -clear_source_info (TreeViewDragInfo *di) +/** + * gtk_tree_view_move_column: + * @tree_view: A #GtkTreeView + * @column: The #GtkTreeViewColumn to be moved. + * @base_column: The #GtkTreeViewColumn to be moved relative to. + * + * Moves @column to be after to @base_column. If @base_column is NULL, then + * @column is placed in the first position. + **/ +void +gtk_tree_view_move_column_after (GtkTreeView *tree_view, + GtkTreeViewColumn *column, + GtkTreeViewColumn *base_column) { - if (di->source_target_list) - gtk_target_list_unref (di->source_target_list); - - if (di->row_draggable_closure) - g_closure_unref (di->row_draggable_closure); - - di->source_target_list = NULL; - di->row_draggable_closure = NULL; + /* FIXME: right this function. */ + g_warning ("FIXME: needs implementing\n"); } -static void -clear_dest_info (TreeViewDragInfo *di) +/** + * gtk_tree_view_set_expander_column: + * @tree_view: A #GtkTreeView + * @col: The column to draw the expander arrow at. + * + * Sets the column offset to draw the expander arrow at. + **/ +void +gtk_tree_view_set_expander_column (GtkTreeView *tree_view, + gint col) { - if (di->location_droppable_closure) - g_closure_unref (di->location_droppable_closure); - - if (di->dest_target_list) - gtk_target_list_unref (di->dest_target_list); + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - di->location_droppable_closure = NULL; - di->dest_target_list = NULL; -} + if (tree_view->priv->expander_column != col) + { + tree_view->priv->expander_column = col; -static void -destroy_info (TreeViewDragInfo *di) -{ - clear_source_info (di); - clear_dest_info (di); - g_free (di); + g_object_notify (G_OBJECT (tree_view), "expander_column"); + } } -static TreeViewDragInfo* -ensure_info (GtkTreeView *tree_view) +/** + * gtk_tree_view_get_expander_column: + * @tree_view: + * + * Returns the offset of the column that is the current expander column. This + * column has the expander arrow drawn next to it. + * + * Return value: The offset of the expander column. + **/ +gint +gtk_tree_view_get_expander_column (GtkTreeView *tree_view) { - TreeViewDragInfo *di; + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), -1); - di = get_info (tree_view); + return tree_view->priv->expander_column; +} - if (di == NULL) - { - di = g_new0 (TreeViewDragInfo, 1); +/** + * gtk_tree_view_scroll_to_point: + * @tree_view: a #GtkTreeView + * @tree_x: X coordinate of new top-left pixel of visible area + * @tree_y: Y coordinate of new top-left pixel of visible area + * + * Scrolls the tree view such that the top-left corner of the visible + * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified + * in tree window coordinates. The @tree_view must be realized before + * this function is called. If it isn't, you probably want ot be + * using gtk_tree_view_scroll_to_cell. + **/ +void +gtk_tree_view_scroll_to_point (GtkTreeView *tree_view, + gint tree_x, + gint tree_y) +{ + GtkAdjustment *hadj; + GtkAdjustment *vadj; - g_object_set_data_full (G_OBJECT (tree_view), - "gtk-tree-view-drag-info", - di, - (GDestroyNotify) destroy_info); - } + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (GTK_WIDGET_REALIZED (tree_view)); - return di; -} + hadj = tree_view->priv->hadjustment; + vadj = tree_view->priv->vadjustment; -static void -remove_info (GtkTreeView *tree_view) -{ - g_object_set_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info", NULL); + gtk_adjustment_set_value (hadj, CLAMP (tree_x, hadj->lower, hadj->upper)); + gtk_adjustment_set_value (vadj, CLAMP (tree_y, vadj->lower, vadj->upper)); } -#define SCROLL_EDGE_SIZE 15 - -static gint -drag_scan_timeout (gpointer data) +/** + * gtk_tree_view_scroll_to_cell + * @tree_view: A #GtkTreeView. + * @path: The path of the row to move to. + * @column: The #GtkTreeViewColumn to move horizontally to. + * @row_align: The vertical alignment of the row specified by @path. + * @col_align: The horizontal alignment of the column specified by @column. + * + * Moves the alignments of @tree_view to the position specified by + * @column and @path. If @column is NULL, then no horizontal + * scrolling occurs. Likewise, if @path is NULL no vertical scrolling + * occurs. @row_align determines where the row is placed, and + * @col_align determines where @column is placed. Both are expected + * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means + * right/bottom alignment, 0.5 means center. + **/ +void +gtk_tree_view_scroll_to_cell (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gfloat row_align, + gfloat col_align) { - GtkTreeView *tree_view; - gint x, y; - GdkModifierType state; - GtkTreePath *path = NULL; - GtkTreeViewColumn *column = NULL; - GdkRectangle visible_rect; + GdkRectangle cell_rect; + GdkRectangle vis_rect; + gint dest_x, dest_y; - tree_view = GTK_TREE_VIEW (data); + /* FIXME work on unmapped/unrealized trees? maybe implement when + * we do incremental reflow for trees + */ - gdk_window_get_pointer (tree_view->priv->bin_window, - &x, &y, &state); + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (row_align >= 0.0); + g_return_if_fail (row_align <= 1.0); + g_return_if_fail (col_align >= 0.0); + g_return_if_fail (col_align <= 1.0); + g_return_if_fail (path != NULL || column != NULL); - gtk_tree_view_get_visible_rect (tree_view, &visible_rect); + row_align = CLAMP (row_align, 0.0, 1.0); + col_align = CLAMP (col_align, 0.0, 1.0); - /* See if we are near the edge. */ - if ((x - visible_rect.x) < SCROLL_EDGE_SIZE || - (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE || - (y - visible_rect.y) < SCROLL_EDGE_SIZE || - (visible_rect.y + visible_rect.height - y) < SCROLL_EDGE_SIZE) + if (! GTK_WIDGET_REALIZED (tree_view)) { - gtk_tree_view_get_path_at_pos (tree_view, - tree_view->priv->bin_window, - x, y, - &path, - &column, - NULL, - NULL); - - if (path != NULL) - { - gtk_tree_view_scroll_to_cell (tree_view, - path, - column, - 0.5, 0.5); + if (path) + tree_view->priv->scroll_to_path = gtk_tree_path_copy (path); + if (column) + tree_view->priv->scroll_to_column = column; + tree_view->priv->scroll_to_row_align = row_align; + tree_view->priv->scroll_to_col_align = col_align; - gtk_tree_path_free (path); - } + return; } - return TRUE; -} + gtk_tree_view_get_cell_area (tree_view, path, column, &cell_rect); + gtk_tree_view_get_visible_rect (tree_view, &vis_rect); + dest_x = vis_rect.x; + dest_y = vis_rect.y; -static void -ensure_scroll_timeout (GtkTreeView *tree_view) -{ - if (tree_view->priv->scroll_timeout == 0) - tree_view->priv->scroll_timeout = gtk_timeout_add (50, drag_scan_timeout, tree_view); -} + if (path) + { + dest_x = cell_rect.x + + cell_rect.width * row_align - + vis_rect.width * row_align; + } -static void -remove_scroll_timeout (GtkTreeView *tree_view) -{ - if (tree_view->priv->scroll_timeout != 0) + if (column) { - gtk_timeout_remove (tree_view->priv->scroll_timeout); - tree_view->priv->scroll_timeout = 0; + dest_y = cell_rect.y + + cell_rect.height * col_align - + vis_rect.height * col_align; } + + gtk_tree_view_scroll_to_point (tree_view, dest_x, dest_y); } + +/** + * gtk_tree_view_row_activated: + * @tree_view: A #GtkTreeView + * @path: The #GtkTreePath to be activated. + * @column: The #GtkTreeViewColumn to be activated. + * + * Activates the cell determined by @path and @column. + **/ void -gtk_tree_view_set_rows_drag_source (GtkTreeView *tree_view, - GdkModifierType start_button_mask, - const GtkTargetEntry *targets, - gint n_targets, - GdkDragAction actions, - GtkTreeViewDraggableFunc row_draggable_func, - gpointer user_data) +gtk_tree_view_row_activated (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column) { - TreeViewDragInfo *di; - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - di = ensure_info (tree_view); - clear_source_info (di); + /* FIXME: Actually activate the path internally, not just emit the signal */ + g_warning ("FIXME: Actually activate the path internally, not just emit the signal\n"); + g_signal_emit (G_OBJECT(tree_view), tree_view_signals[ROW_ACTIVATED], 0, path, column); +} - di->start_button_mask = start_button_mask; - di->source_target_list = gtk_target_list_new (targets, n_targets); - di->source_actions = actions; - if (row_draggable_func) +static void +gtk_tree_view_expand_all_helper (GtkRBTree *tree, + GtkRBNode *node, + gpointer data) +{ + GtkTreeView *tree_view = data; + + if (node->children) + _gtk_rbtree_traverse (node->children, + node->children->root, + G_PRE_ORDER, + gtk_tree_view_expand_all_helper, + data); + else if ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT && node->children == NULL) { - di->row_draggable_closure = g_cclosure_new ((GCallback) row_draggable_func, - user_data, NULL); - g_closure_ref (di->row_draggable_closure); - g_closure_sink (di->row_draggable_closure); - } + GtkTreePath *path; + GtkTreeIter iter; + GtkTreeIter child; - di->source_set = TRUE; + node->children = _gtk_rbtree_new (); + node->children->parent_tree = tree; + node->children->parent_node = node; + path = _gtk_tree_view_find_path (tree_view, tree, node); + gtk_tree_model_get_iter (tree_view->priv->model, &iter, path); + gtk_tree_model_iter_children (tree_view->priv->model, &child, &iter); + gtk_tree_view_build_tree (tree_view, + node->children, + &child, + gtk_tree_path_get_depth (path) + 1, + TRUE, + GTK_WIDGET_REALIZED (tree_view)); + gtk_tree_path_free (path); + } } +/** + * gtk_tree_view_expand_all: + * @tree_view: A #GtkTreeView. + * + * Recursively expands all nodes in the @tree_view. + **/ void -gtk_tree_view_set_rows_drag_dest (GtkTreeView *tree_view, - const GtkTargetEntry *targets, - gint n_targets, - GdkDragAction actions, - GtkTreeViewDroppableFunc location_droppable_func, - gpointer user_data) +gtk_tree_view_expand_all (GtkTreeView *tree_view) { - TreeViewDragInfo *di; - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (tree_view->priv->tree != NULL); - gtk_drag_dest_set (GTK_WIDGET (tree_view), - 0, - NULL, - 0, - actions); - - di = ensure_info (tree_view); - clear_dest_info (di); - - if (targets) - di->dest_target_list = gtk_target_list_new (targets, n_targets); - - if (location_droppable_func) - { - di->location_droppable_closure = g_cclosure_new ((GCallback) location_droppable_func, - user_data, NULL); - g_closure_ref (di->location_droppable_closure); - g_closure_sink (di->location_droppable_closure); - } + _gtk_rbtree_traverse (tree_view->priv->tree, + tree_view->priv->tree->root, + G_PRE_ORDER, + gtk_tree_view_expand_all_helper, + tree_view); - di->dest_set = TRUE; + _gtk_tree_view_update_size (tree_view); } -void -gtk_tree_view_unset_rows_drag_source (GtkTreeView *tree_view) +static void +gtk_tree_view_collapse_all_helper (GtkRBTree *tree, + GtkRBNode *node, + gpointer data) { - TreeViewDragInfo *di; - - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + if (node->children) + { + GtkTreePath *path; + GtkTreeIter iter; - di = get_info (tree_view); + path = _gtk_tree_view_find_path (GTK_TREE_VIEW (data), + node->children, + node->children->root); + gtk_tree_model_get_iter (GTK_TREE_VIEW (data)->priv->model, + &iter, + path); + gtk_tree_view_discover_dirty (GTK_TREE_VIEW (data), + node->children, + &iter, + gtk_tree_path_get_depth (path)); - if (di) - { - if (di->source_set) - { - clear_source_info (di); - di->source_set = FALSE; - } + /* Ensure we don't have a dangling pointer to a dead node */ + ensure_unprelighted (GTK_TREE_VIEW (data)); - if (!di->dest_set && !di->source_set) - remove_info (tree_view); + _gtk_rbtree_remove (node->children); + gtk_tree_path_free (path); } } +/** + * gtk_tree_view_collapse_all: + * @tree_view: A #GtkTreeView. + * + * Recursively collapses all visible, expanded nodes in @tree_view. + **/ void -gtk_tree_view_unset_rows_drag_dest (GtkTreeView *tree_view) +gtk_tree_view_collapse_all (GtkTreeView *tree_view) { - TreeViewDragInfo *di; - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (tree_view->priv->tree != NULL); - di = get_info (tree_view); - - if (di) - { - if (di->dest_set) - { - gtk_drag_dest_unset (GTK_WIDGET (tree_view)); - clear_dest_info (di); - di->dest_set = FALSE; - } + _gtk_rbtree_traverse (tree_view->priv->tree, + tree_view->priv->tree->root, + G_PRE_ORDER, + gtk_tree_view_collapse_all_helper, + tree_view); - if (!di->dest_set && !di->source_set) - remove_info (tree_view); - } + if (GTK_WIDGET_MAPPED (tree_view)) + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); } -void -gtk_tree_view_set_drag_dest_row (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewDropPosition pos) +/* FIXME the bool return values for expand_row and collapse_row are + * not analagous; they should be TRUE if the row had children and + * was not already in the requested state. + */ + +/** + * gtk_tree_view_expand_row: + * @tree_view: a #GtkTreeView + * @path: path to a row + * @open_all: whether to recursively expand, or just expand immediate children + * + * Opens the row so its children are visible + * + * Return value: %TRUE if the row existed and had children + **/ +gboolean +gtk_tree_view_expand_row (GtkTreeView *tree_view, + GtkTreePath *path, + gboolean open_all) { - GtkTreePath *current_dest; - /* Note; this function is exported to allow a custom DND - * implementation, so it can't touch TreeViewDragInfo - */ + GtkTreeIter iter; + GtkTreeIter child; + GtkRBTree *tree; + GtkRBNode *node; + gboolean expand; - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); + g_return_val_if_fail (tree_view->priv->model != NULL, FALSE); + g_return_val_if_fail (path != NULL, FALSE); - current_dest = NULL; + if (_gtk_tree_view_find_node (tree_view, + path, + &tree, + &node)) + return FALSE; - if (tree_view->priv->drag_dest_row) - current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row); + if (node->children) + return TRUE; - if (current_dest) - { - gtk_tree_view_queue_draw_path (tree_view, current_dest, NULL); - gtk_tree_path_free (current_dest); - } + gtk_tree_model_get_iter (tree_view->priv->model, &iter, path); + if (! gtk_tree_model_iter_has_child (tree_view->priv->model, &iter)) + return FALSE; - if (tree_view->priv->drag_dest_row) - gtk_tree_row_reference_free (tree_view->priv->drag_dest_row); + g_signal_emit (G_OBJECT (tree_view), tree_view_signals[EXPAND_ROW], 0, &iter, path, &expand); - tree_view->priv->drag_dest_pos = pos; + if (expand) + return FALSE; - if (path) - { - tree_view->priv->drag_dest_row = - gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path); - gtk_tree_view_queue_draw_path (tree_view, path, NULL); - } - else - tree_view->priv->drag_dest_row = NULL; -} + node->children = _gtk_rbtree_new (); + node->children->parent_tree = tree; + node->children->parent_node = node; -void -gtk_tree_view_get_drag_dest_row (GtkTreeView *tree_view, - GtkTreePath **path, - GtkTreeViewDropPosition *pos) -{ - g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + gtk_tree_model_iter_children (tree_view->priv->model, &child, &iter); + gtk_tree_view_build_tree (tree_view, + node->children, + &child, + gtk_tree_path_get_depth (path) + 1, + open_all, + GTK_WIDGET_REALIZED (tree_view)); - if (path) - { - if (tree_view->priv->drag_dest_row) - *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row); - else - *path = NULL; - } + if (GTK_WIDGET_MAPPED (tree_view)) + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - if (pos) - *pos = tree_view->priv->drag_dest_pos; + return TRUE; } +/** + * gtk_tree_view_collapse_row: + * @tree_view: a #GtkTreeView + * @path: path to a row in the @tree_view + * + * Collapses a row (hides its child rows, if they exist.) + * + * Return value: %TRUE if the row was collapsed. + **/ gboolean -gtk_tree_view_get_dest_row_at_pos (GtkTreeView *tree_view, - gint drag_x, - gint drag_y, - GtkTreePath **path, - GtkTreeViewDropPosition *pos) +gtk_tree_view_collapse_row (GtkTreeView *tree_view, + GtkTreePath *path) { - gint cell_y; - gdouble offset_into_row; - gdouble quarter; - gint x, y; - GdkRectangle cell; - GtkTreeViewColumn *column = NULL; - GtkTreePath *tmp_path = NULL; - - /* Note; this function is exported to allow a custom DND - * implementation, so it can't touch TreeViewDragInfo - */ + GtkRBTree *tree; + GtkRBNode *node; + GtkTreeIter iter; + gboolean collapse; - g_return_val_if_fail (tree_view != NULL, FALSE); + g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE); g_return_val_if_fail (tree_view->priv->tree != NULL, FALSE); - g_return_val_if_fail (drag_x >= 0, FALSE); - g_return_val_if_fail (drag_y >= 0, FALSE); - g_return_val_if_fail (tree_view->priv->bin_window != NULL, FALSE); + g_return_val_if_fail (path != NULL, FALSE); - if (path) - *path = NULL; + if (_gtk_tree_view_find_node (tree_view, + path, + &tree, + &node)) + return FALSE; - /* remember that drag_x and drag_y are in widget coords, convert to tree window */ + if (node->children == NULL) + return FALSE; - gtk_tree_view_widget_to_tree_coords (tree_view, drag_x, drag_y, - &x, &y); + gtk_tree_model_get_iter (tree_view->priv->model, &iter, path); - /* If in the top quarter of a row, we drop before that row; if - * in the bottom quarter, drop after that row; if in the middle, - * and the row has children, drop into the row. - */ + g_signal_emit (G_OBJECT (tree_view), tree_view_signals[COLLAPSE_ROW], 0, &iter, path, &collapse); - if (!gtk_tree_view_get_path_at_pos (tree_view, - tree_view->priv->bin_window, - x, y, - &tmp_path, - &column, - NULL, - &cell_y)) + if (collapse) return FALSE; - gtk_tree_view_get_background_area (tree_view, tmp_path, column, - &cell); + gtk_tree_view_discover_dirty (tree_view, + node->children, + &iter, + gtk_tree_path_get_depth (path)); - offset_into_row = cell_y; + /* Ensure we don't have a dangling pointer to a dead node */ + ensure_unprelighted (tree_view); - if (path) - *path = tmp_path; - else - gtk_tree_path_free (tmp_path); + g_assert (tree_view->priv->prelight_node == NULL); - tmp_path = NULL; + _gtk_rbtree_remove (node->children); - quarter = cell.height / 4.0; + if (GTK_WIDGET_MAPPED (tree_view)) + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - if (pos) - { - if (offset_into_row < quarter) - { - *pos = GTK_TREE_VIEW_DROP_BEFORE; - } - else if (offset_into_row < quarter * 2) - { - *pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE; - } - else if (offset_into_row < quarter * 3) - { - *pos = GTK_TREE_VIEW_DROP_INTO_OR_AFTER; - } - else - { - *pos = GTK_TREE_VIEW_DROP_AFTER; - } - } + return TRUE; +} + + +/** + * gtk_tree_view_map_expanded_row: + * @tree_view: A #GtkTreeView + * @func: A function to be called + * @data: User data to be passed to the function. + * + * Calls @func on all expanded rows. + **/ +void +gtk_tree_view_map_expanded_rows (GtkTreeView *tree_view, + GtkTreeViewMappingFunc func, + gpointer data) +{ + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (func != NULL); - return TRUE; + /* FIXME: actually implement this function */ + g_warning ("FIXME: actually implement this function"); } -static gboolean -gtk_tree_view_maybe_begin_dragging_row (GtkTreeView *tree_view, - GdkEventMotion *event) + +/** + * gtk_tree_view_get_path_at_pos: + * @tree_view: A #GtkTreeView. + * @window: The #GdkWindow to check against. + * @x: The x position to be identified. + * @y: The y position to be identified. + * @path: A pointer to a #GtkTreePath pointer to be filled in, or %NULL + * @column: A pointer to a #GtkTreeViewColumn pointer to be filled in, or %NULL + * @cell_x: A pointer where the X coordinate relative to the cell can be placed, or %NULL + * @cell_y: A pointer where the Y coordinate relative to the cell can be placed, or %NULL + * + * Finds the path at the point (@x, @y) relative to @window. If @window is + * NULL, then the point is found relative to the widget coordinates. This + * function is expected to be called after an event, with event->window being + * passed in as @window. It is primarily for things like popup menus. If @path + * is non-NULL, then it will be filled with the #GtkTreePath at that point. + * This path should be freed with #gtk_tree_path_free. If @column is non-NULL, + * then it will be filled with the column at that point. @cell_x and @cell_y + * return the coordinates relative to the cell background (i.e. the + * background_area passed to gtk_cell_renderer_render()). This function only + * works if @tree_view is realized. + * + * Return value: TRUE if a row exists at that coordinate. + **/ +gboolean +gtk_tree_view_get_path_at_pos (GtkTreeView *tree_view, + GdkWindow *window, + gint x, + gint y, + GtkTreePath **path, + GtkTreeViewColumn **column, + gint *cell_x, + gint *cell_y) { - GdkDragContext *context; - TreeViewDragInfo *di; - GtkTreePath *path = NULL; - gint button; - gint cell_x, cell_y; - GtkTreeModel *model; + GtkRBTree *tree; + GtkRBNode *node; + gint y_offset; - di = get_info (tree_view); + g_return_val_if_fail (tree_view != NULL, FALSE); + g_return_val_if_fail (tree_view->priv->tree != NULL, FALSE); + g_return_val_if_fail (tree_view->priv->bin_window != NULL, FALSE); - if (di == NULL) - return FALSE; + if (window) + g_return_val_if_fail (window == tree_view->priv->bin_window, FALSE); - if (tree_view->priv->pressed_button < 0) - return FALSE; + if (path) + *path = NULL; + if (column) + *column = NULL; - if (!gtk_drag_check_threshold (GTK_WIDGET (tree_view), - tree_view->priv->press_start_x, - tree_view->priv->press_start_y, - event->x, event->y)) + if (x > tree_view->priv->hadjustment->upper) return FALSE; - model = gtk_tree_view_get_model (tree_view); - - if (model == NULL) + if (x < 0 || y < 0) return FALSE; - button = tree_view->priv->pressed_button; - tree_view->priv->pressed_button = -1; + if (column || cell_x) + { + GtkTreeViewColumn *tmp_column; + GtkTreeViewColumn *last_column = NULL; + GList *list; + gint remaining_x = x; + gboolean found = FALSE; - gtk_tree_view_get_path_at_pos (tree_view, - tree_view->priv->bin_window, - tree_view->priv->press_start_x, - tree_view->priv->press_start_y, - &path, - NULL, - &cell_x, - &cell_y); + for (list = tree_view->priv->columns; list; list = list->next) + { + tmp_column = list->data; - if (path == NULL) - return FALSE; + if (tmp_column->visible == FALSE) + continue; - /* FIXME if the path doesn't match the row_draggable predicate, - * return FALSE and free path - */ + last_column = tmp_column; + if (remaining_x <= tmp_column->width) + { + found = TRUE; - /* FIXME Check whether we're a start button, if not return FALSE and - * free path - */ + if (column) + *column = tmp_column; - context = gtk_drag_begin (GTK_WIDGET (tree_view), - di->source_target_list, - di->source_actions, - button, - (GdkEvent*)event); + if (cell_x) + *cell_x = remaining_x; - gtk_drag_set_icon_default (context); + break; + } + remaining_x -= tmp_column->width; + } - { - GdkPixmap *row_pix; + if (!found) + { + if (column) + *column = last_column; - row_pix = gtk_tree_view_create_row_drag_icon (tree_view, - path); + if (cell_x) + *cell_x = last_column->width + remaining_x; + } + } - gtk_drag_set_icon_pixmap (context, - gdk_drawable_get_colormap (row_pix), - row_pix, - NULL, - /* the + 1 is for the black border in the icon */ - tree_view->priv->press_start_x + 1, - cell_y + 1); + if (window) + { + y_offset = _gtk_rbtree_find_offset (tree_view->priv->tree, + TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y), + &tree, &node); + } + else + { + if (y < TREE_VIEW_HEADER_HEIGHT (tree_view)) + return FALSE; - gdk_pixmap_unref (row_pix); - } + y_offset = _gtk_rbtree_find_offset (tree_view->priv->tree, + TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y + tree_view->priv->vadjustment->value), + &tree, &node); + } - set_source_row (context, model, path); - gtk_tree_path_free (path); + if (tree == NULL) + return FALSE; - return TRUE; -} + if (cell_y) + *cell_y = y_offset; -/* Default signal implementations for the drag signals */ + if (path) + *path = _gtk_tree_view_find_path (tree_view, tree, node); -static void -gtk_tree_view_drag_begin (GtkWidget *widget, - GdkDragContext *context) -{ - /* do nothing */ + return TRUE; } -static void -gtk_tree_view_drag_end (GtkWidget *widget, - GdkDragContext *context) -{ - /* do nothing */ -} -static void -gtk_tree_view_drag_data_get (GtkWidget *widget, - GdkDragContext *context, - GtkSelectionData *selection_data, - guint info, - guint time) +/** + * gtk_tree_view_get_cell_area: + * @tree_view: a #GtkTreeView + * @path: a #GtkTreePath for the row, or %NULL to get only horizontal coordinates + * @column: a #GtkTreeViewColumn for the column, or %NULL to get only vertical coordiantes + * @rect: rectangle to fill with cell rect + * + * Fills the bounding rectangle in tree window coordinates for the cell at the + * row specified by @path and the column specified by @column. If @path is + * %NULL, the y and height fields of the rectangle will be filled with 0. If + * @column is %NULL, the x and width fields will be filled with 0. The sum of + * all cell rects does not cover the entire tree; there are extra pixels in + * between rows, for example. The returned rectangle is equivalent to the + * @cell_area passed to gtk_cell_renderer_render(). This function is only valid + * if #tree_view is realized. + **/ +void +gtk_tree_view_get_cell_area (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + GdkRectangle *rect) { - GtkTreeView *tree_view; - GtkTreeModel *model; - TreeViewDragInfo *di; - GtkTreePath *source_row; - - tree_view = GTK_TREE_VIEW (widget); - - model = gtk_tree_view_get_model (tree_view); - - if (model == NULL) - return; - - di = get_info (GTK_TREE_VIEW (widget)); + GtkRBTree *tree = NULL; + GtkRBNode *node = NULL; + gint vertical_separator; - if (di == NULL) - return; + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column)); + g_return_if_fail (rect != NULL); - source_row = get_source_row (context); + gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical_separator", &vertical_separator, NULL); - if (source_row == NULL) - return; + rect->x = 0; + rect->y = 0; + rect->width = 0; + rect->height = 0; - /* We can implement the GTK_TREE_MODEL_ROW target generically for - * any model; for DragSource models there are some other targets - * we also support. - */ + if (path) + { + /* Get vertical coords */ - if (GTK_IS_TREE_DRAG_SOURCE (model) && - gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model), - source_row, - selection_data)) - goto done; + _gtk_tree_view_find_node (tree_view, path, &tree, &node); - /* If drag_data_get does nothing, try providing row data. */ - if (selection_data->target == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE)) - { - gtk_selection_data_set_tree_row (selection_data, - model, - source_row); - } + if (tree == NULL) + { + g_warning (G_STRLOC": no row corresponding to path"); + return; + } - done: - gtk_tree_path_free (source_row); -} + /* Remember that the rbtree stores node height including the vertical + * separator, see comment at top of file. + */ + rect->y = CELL_FIRST_PIXEL (tree_view, tree, node, vertical_separator); + rect->height = CELL_HEIGHT (node, vertical_separator); + } -static gboolean -check_model_dnd (GtkTreeModel *model, - GType required_iface, - const gchar *signal) -{ - if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface)) + if (column) { - g_warning ("You must override the default '%s' handler " - "on GtkTreeView when using models that don't support " - "the %s interface and enabling drag-and-drop. The simplest way to do this " - "is to connect to '%s' and call " - "gtk_signal_emit_stop_by_name() in your signal handler to prevent " - "the default handler from running. Look at the source code " - "for the default handler in gtktreeview.c to get an idea what " - "your handler should do. (gtktreeview.c is in the GTK source " - "code.) If you're using GTK from a language other than C, " - "there may be a more natural way to override default handlers, e.g. via derivation.", - signal, g_type_name (required_iface), signal); - return FALSE; + gint x2 = 0; + + gtk_tree_view_get_cell_xrange (tree_view, tree, column, &rect->x, &x2); + rect->width = x2 - rect->x; } - else - return TRUE; } -static void -gtk_tree_view_drag_data_delete (GtkWidget *widget, - GdkDragContext *context) +/** + * gtk_tree_view_get_background_area: + * @tree_view: a #GtkTreeView + * @path: a #GtkTreePath for the row, or %NULL to get only horizontal coordinates + * @column: a #GtkTreeViewColumn for the column, or %NULL to get only vertical coordiantes + * @rect: rectangle to fill with cell background rect + * + * Fills the bounding rectangle in tree window coordinates for the + * cell at the row specified by @path and the column specified by + * @column. If @path is %NULL, the y and height fields of the + * rectangle will be filled with 0. If @column is %NULL, the x and + * width fields will be filled with 0. The returned rectangle is + * equivalent to the @background_area passed to + * gtk_cell_renderer_render(). These background areas tile to cover + * the entire tree window (except for the area used for header + * buttons). Contrast with the cell_area, returned by + * gtk_tree_view_get_cell_area(), which returns only the cell itself, + * excluding surrounding borders and the tree expander area. + * + **/ +void +gtk_tree_view_get_background_area (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + GdkRectangle *rect) { - TreeViewDragInfo *di; - GtkTreeModel *model; - GtkTreeView *tree_view; - GtkTreePath *source_row; - - tree_view = GTK_TREE_VIEW (widget); - model = gtk_tree_view_get_model (tree_view); - - if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete")) - return; + GtkRBTree *tree = NULL; + GtkRBNode *node = NULL; - di = get_info (tree_view); + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_return_if_fail (column == NULL || GTK_IS_TREE_VIEW_COLUMN (column)); + g_return_if_fail (rect != NULL); - if (di == NULL) - return; + rect->x = 0; + rect->y = 0; + rect->width = 0; + rect->height = 0; - source_row = get_source_row (context); + if (path) + { + /* Get vertical coords */ - if (source_row == NULL) - return; + _gtk_tree_view_find_node (tree_view, path, &tree, &node); - gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model), - source_row); + if (tree == NULL) + { + g_warning (G_STRLOC": no row corresponding to path"); + return; + } - gtk_tree_path_free (source_row); + rect->y = BACKGROUND_FIRST_PIXEL (tree_view, tree, node); - set_source_row (context, NULL, NULL); -} + rect->height = BACKGROUND_HEIGHT (node); + } -static void -remove_open_timeout (GtkTreeView *tree_view) -{ - if (tree_view->priv->open_dest_timeout != 0) + if (column) { - gtk_timeout_remove (tree_view->priv->open_dest_timeout); - tree_view->priv->open_dest_timeout = 0; + gint x2 = 0; + + gtk_tree_view_get_background_xrange (tree_view, tree, column, &rect->x, &x2); + rect->width = x2 - rect->x; } } -static void -gtk_tree_view_drag_leave (GtkWidget *widget, - GdkDragContext *context, - guint time) +/** + * gtk_tree_view_get_visible_rect: + * @tree_view: a #GtkTreeView + * @visible_rect: rectangle to fill + * + * Fills @visible_rect with the currently-visible region of the + * buffer, in tree coordinates. Convert to widget coordinates with + * gtk_tree_view_tree_to_widget_coords(). Tree coordinates start at + * 0,0 for row 0 of the tree, and cover the entire scrollable area of + * the tree. + **/ +void +gtk_tree_view_get_visible_rect (GtkTreeView *tree_view, + GdkRectangle *visible_rect) { - TreeViewDragInfo *di; + GtkWidget *widget; - di = get_info (GTK_TREE_VIEW (widget)); + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - /* unset any highlight row */ - gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), - NULL, - GTK_TREE_VIEW_DROP_BEFORE); + widget = GTK_WIDGET (tree_view); - remove_scroll_timeout (GTK_TREE_VIEW (widget)); - remove_open_timeout (GTK_TREE_VIEW (widget)); + if (visible_rect) + { + visible_rect->x = tree_view->priv->hadjustment->value; + visible_rect->y = tree_view->priv->vadjustment->value; + visible_rect->width = widget->allocation.width; + visible_rect->height = widget->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view); + } } -static gint -open_row_timeout (gpointer data) +/** + * gtk_tree_view_widget_to_tree_coords: + * @tree_view: a #GtkTreeView + * @wx: widget X coordinate + * @wy: widget Y coordinate + * @tx: return location for tree X coordinate + * @ty: return location for tree Y coordinate + * + * Converts widget coordinates to coordinates for the + * tree window (the full scrollable area of the tree). + * + **/ +void +gtk_tree_view_widget_to_tree_coords (GtkTreeView *tree_view, + gint wx, + gint wy, + gint *tx, + gint *ty) { - GtkTreeView *tree_view = data; - GtkTreePath *dest_path = NULL; - GtkTreeViewDropPosition pos; - - gtk_tree_view_get_drag_dest_row (tree_view, - &dest_path, - &pos); + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - if (dest_path && - (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER || - pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) + if (tx) { - gtk_tree_view_expand_row (tree_view, dest_path, FALSE); - tree_view->priv->open_dest_timeout = 0; - - gtk_tree_path_free (dest_path); - - return FALSE; + *tx = wx + tree_view->priv->hadjustment->value; } - else + + if (ty) { - if (dest_path) - gtk_tree_path_free (dest_path); - return TRUE; + *ty = wy + tree_view->priv->vadjustment->value; } } -/* Returns TRUE if event should not be propagated to parent widgets */ -static gboolean -set_destination_row (GtkTreeView *tree_view, - GdkDragContext *context, - gint x, - gint y, - GdkDragAction *suggested_action, - GdkAtom *target) +/** + * gtk_tree_view_tree_to_widget_coords: + * @tree_view: a #GtkTreeView + * @tx: tree X coordinate + * @ty: tree Y coordinate + * @wx: return location for widget X coordinate + * @wy: return location for widget Y coordinate + * + * Converts tree coordinates (coordinates in full scrollable area of the tree) + * to widget coordinates. + * + **/ +void +gtk_tree_view_tree_to_widget_coords (GtkTreeView *tree_view, + gint tx, + gint ty, + gint *wx, + gint *wy) { - GtkTreePath *path = NULL; - GtkTreeViewDropPosition pos; - GtkTreeViewDropPosition old_pos; - TreeViewDragInfo *di; - GtkWidget *widget; - GtkTreePath *old_dest_path = NULL; + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - *suggested_action = 0; - *target = GDK_NONE; + if (wx) + { + *wx = tx - tree_view->priv->hadjustment->value; + } - widget = GTK_WIDGET (tree_view); + if (wy) + { + *wy = ty - tree_view->priv->vadjustment->value; + } +} - di = get_info (tree_view); - if (di == NULL) - { - /* someone unset us as a drag dest, note that if - * we return FALSE drag_leave isn't called - */ +void +gtk_tree_view_set_rows_drag_source (GtkTreeView *tree_view, + GdkModifierType start_button_mask, + const GtkTargetEntry *targets, + gint n_targets, + GdkDragAction actions, + GtkTreeViewDraggableFunc row_draggable_func, + gpointer user_data) +{ + TreeViewDragInfo *di; - gtk_tree_view_set_drag_dest_row (tree_view, - NULL, - GTK_TREE_VIEW_DROP_BEFORE); + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - remove_scroll_timeout (GTK_TREE_VIEW (widget)); - remove_open_timeout (GTK_TREE_VIEW (widget)); + di = ensure_info (tree_view); + clear_source_info (di); - return FALSE; /* no longer a drop site */ - } + di->start_button_mask = start_button_mask; + di->source_target_list = gtk_target_list_new (targets, n_targets); + di->source_actions = actions; - *target = gtk_drag_dest_find_target (widget, context, di->dest_target_list); - if (*target == GDK_NONE) + if (row_draggable_func) { - return FALSE; + di->row_draggable_closure = g_cclosure_new ((GCallback) row_draggable_func, + user_data, NULL); + g_closure_ref (di->row_draggable_closure); + g_closure_sink (di->row_draggable_closure); } - if (!gtk_tree_view_get_dest_row_at_pos (tree_view, - x, y, - &path, - &pos)) - { - /* can't drop here */ - remove_open_timeout (tree_view); - - gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), - NULL, - GTK_TREE_VIEW_DROP_BEFORE); + di->source_set = TRUE; +} - /* don't propagate to parent though */ - return TRUE; - } +void +gtk_tree_view_set_rows_drag_dest (GtkTreeView *tree_view, + const GtkTargetEntry *targets, + gint n_targets, + GdkDragAction actions, + GtkTreeViewDroppableFunc location_droppable_func, + gpointer user_data) +{ + TreeViewDragInfo *di; - g_assert (path); + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - /* If we left the current row's "open" zone, unset the timeout for - * opening the row - */ - gtk_tree_view_get_drag_dest_row (tree_view, - &old_dest_path, - &old_pos); + gtk_drag_dest_set (GTK_WIDGET (tree_view), + 0, + NULL, + 0, + actions); - if (old_dest_path && - (gtk_tree_path_compare (path, old_dest_path) != 0 || - !(pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER || - pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE))) - remove_open_timeout (tree_view); + di = ensure_info (tree_view); + clear_dest_info (di); - if (old_dest_path) - gtk_tree_path_free (old_dest_path); + if (targets) + di->dest_target_list = gtk_target_list_new (targets, n_targets); - if (TRUE /* FIXME if the location droppable predicate */) + if (location_droppable_func) { - GtkWidget *source_widget; + di->location_droppable_closure = g_cclosure_new ((GCallback) location_droppable_func, + user_data, NULL); + g_closure_ref (di->location_droppable_closure); + g_closure_sink (di->location_droppable_closure); + } - *suggested_action = context->suggested_action; + di->dest_set = TRUE; +} - source_widget = gtk_drag_get_source_widget (context); +void +gtk_tree_view_unset_rows_drag_source (GtkTreeView *tree_view) +{ + TreeViewDragInfo *di; - if (source_widget == widget) - { - /* Default to MOVE, unless the user has - * pressed ctrl or alt to affect available actions - */ - if ((context->actions & GDK_ACTION_MOVE) != 0) - *suggested_action = GDK_ACTION_MOVE; - } + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), - path, pos); - } - else + di = get_info (tree_view); + + if (di) { - /* can't drop here */ - remove_open_timeout (tree_view); + if (di->source_set) + { + clear_source_info (di); + di->source_set = FALSE; + } - gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), - NULL, - GTK_TREE_VIEW_DROP_BEFORE); + if (!di->dest_set && !di->source_set) + remove_info (tree_view); } - - return TRUE; } -static gboolean -gtk_tree_view_drag_motion (GtkWidget *widget, - GdkDragContext *context, - gint x, - gint y, - guint time) +void +gtk_tree_view_unset_rows_drag_dest (GtkTreeView *tree_view) { - GtkTreePath *path = NULL; - GtkTreeViewDropPosition pos; - GtkTreeView *tree_view; - GdkDragAction suggested_action = 0; - GdkAtom target; - - tree_view = GTK_TREE_VIEW (widget); - - if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target)) - return FALSE; + TreeViewDragInfo *di; - ensure_scroll_timeout (tree_view); + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - gtk_tree_view_get_drag_dest_row (tree_view, &path, &pos); + di = get_info (tree_view); - if (path == NULL) - { - /* Can't drop here. */ - gdk_drag_status (context, 0, time); - } - else + if (di) { - if (tree_view->priv->open_dest_timeout == 0 && - (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER || - pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)) + if (di->dest_set) { - tree_view->priv->open_dest_timeout = - gtk_timeout_add (500, open_row_timeout, tree_view); + gtk_drag_dest_unset (GTK_WIDGET (tree_view)); + clear_dest_info (di); + di->dest_set = FALSE; } - if (target == gdk_atom_intern ("GTK_TREE_MODEL_ROW", FALSE)) - { - /* Request data so we can use the source row when - * determining whether to accept the drop - */ - set_status_pending (context, suggested_action); - gtk_drag_get_data (widget, context, target, time); - } - else - { - set_status_pending (context, 0); - gdk_drag_status (context, suggested_action, time); - } + if (!di->dest_set && !di->source_set) + remove_info (tree_view); } +} - if (path) - gtk_tree_path_free (path); +void +gtk_tree_view_set_drag_dest_row (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewDropPosition pos) +{ + GtkTreePath *current_dest; + /* Note; this function is exported to allow a custom DND + * implementation, so it can't touch TreeViewDragInfo + */ - return TRUE; -} + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); -static GtkTreePath* -get_logical_dest_row (GtkTreeView *tree_view) + current_dest = NULL; -{ - /* adjust path to point to the row the drop goes in front of */ - GtkTreePath *path = NULL; - GtkTreeViewDropPosition pos; + if (tree_view->priv->drag_dest_row) + current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row); - gtk_tree_view_get_drag_dest_row (tree_view, &path, &pos); + if (current_dest) + { + gtk_tree_view_queue_draw_path (tree_view, current_dest, NULL); + gtk_tree_path_free (current_dest); + } - if (path == NULL) - return NULL; + if (tree_view->priv->drag_dest_row) + gtk_tree_row_reference_free (tree_view->priv->drag_dest_row); - if (pos == GTK_TREE_VIEW_DROP_BEFORE) - ; /* do nothing */ - else if (pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE || - pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER) + tree_view->priv->drag_dest_pos = pos; + + if (path) { - /* get first child, drop before it */ - gtk_tree_path_append_index (path, 0); + tree_view->priv->drag_dest_row = + gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path); + gtk_tree_view_queue_draw_path (tree_view, path, NULL); } else + tree_view->priv->drag_dest_row = NULL; +} + +void +gtk_tree_view_get_drag_dest_row (GtkTreeView *tree_view, + GtkTreePath **path, + GtkTreeViewDropPosition *pos) +{ + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + + if (path) { - g_assert (pos == GTK_TREE_VIEW_DROP_AFTER); - gtk_tree_path_next (path); + if (tree_view->priv->drag_dest_row) + *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row); + else + *path = NULL; } - return path; + if (pos) + *pos = tree_view->priv->drag_dest_pos; } -static gboolean -gtk_tree_view_drag_drop (GtkWidget *widget, - GdkDragContext *context, - gint x, - gint y, - guint time) +gboolean +gtk_tree_view_get_dest_row_at_pos (GtkTreeView *tree_view, + gint drag_x, + gint drag_y, + GtkTreePath **path, + GtkTreeViewDropPosition *pos) { - GtkTreeView *tree_view; - GtkTreePath *path; - GdkDragAction suggested_action = 0; - GdkAtom target = GDK_NONE; - TreeViewDragInfo *di; - GtkTreeModel *model; + gint cell_y; + gdouble offset_into_row; + gdouble quarter; + gint x, y; + GdkRectangle cell; + GtkTreeViewColumn *column = NULL; + GtkTreePath *tmp_path = NULL; - tree_view = GTK_TREE_VIEW (widget); + /* Note; this function is exported to allow a custom DND + * implementation, so it can't touch TreeViewDragInfo + */ - model = gtk_tree_view_get_model (tree_view); + g_return_val_if_fail (tree_view != NULL, FALSE); + g_return_val_if_fail (tree_view->priv->tree != NULL, FALSE); + g_return_val_if_fail (drag_x >= 0, FALSE); + g_return_val_if_fail (drag_y >= 0, FALSE); + g_return_val_if_fail (tree_view->priv->bin_window != NULL, FALSE); - remove_scroll_timeout (GTK_TREE_VIEW (widget)); - remove_open_timeout (GTK_TREE_VIEW (widget)); + if (path) + *path = NULL; - di = get_info (tree_view); + /* remember that drag_x and drag_y are in widget coords, convert to tree window */ - if (di == NULL) - return FALSE; + gtk_tree_view_widget_to_tree_coords (tree_view, drag_x, drag_y, + &x, &y); - if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop")) - return FALSE; + /* If in the top quarter of a row, we drop before that row; if + * in the bottom quarter, drop after that row; if in the middle, + * and the row has children, drop into the row. + */ - if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target)) + if (!gtk_tree_view_get_path_at_pos (tree_view, + tree_view->priv->bin_window, + x, y, + &tmp_path, + &column, + NULL, + &cell_y)) return FALSE; - path = get_logical_dest_row (tree_view); - - if (target != GDK_NONE && path != NULL) - { - /* in case a motion had requested drag data, change things so we - * treat drag data receives as a drop. - */ - set_status_pending (context, 0); + gtk_tree_view_get_background_area (tree_view, tmp_path, column, + &cell); - set_dest_row (context, model, path); - } + offset_into_row = cell_y; if (path) - gtk_tree_path_free (path); + *path = tmp_path; + else + gtk_tree_path_free (tmp_path); - /* Unset this thing */ - gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), - NULL, - GTK_TREE_VIEW_DROP_BEFORE); + tmp_path = NULL; - if (target != GDK_NONE) + quarter = cell.height / 4.0; + + if (pos) { - gtk_drag_get_data (widget, context, target, time); - return TRUE; + if (offset_into_row < quarter) + { + *pos = GTK_TREE_VIEW_DROP_BEFORE; + } + else if (offset_into_row < quarter * 2) + { + *pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE; + } + else if (offset_into_row < quarter * 3) + { + *pos = GTK_TREE_VIEW_DROP_INTO_OR_AFTER; + } + else + { + *pos = GTK_TREE_VIEW_DROP_AFTER; + } } - else - return FALSE; + + return TRUE; } -static void -gtk_tree_view_drag_data_received (GtkWidget *widget, - GdkDragContext *context, - gint x, - gint y, - GtkSelectionData *selection_data, - guint info, - guint time) -{ - GtkTreePath *path; - TreeViewDragInfo *di; - gboolean accepted = FALSE; - GtkTreeModel *model; - GtkTreeView *tree_view; - GtkTreePath *dest_row; - GdkDragAction suggested_action; - tree_view = GTK_TREE_VIEW (widget); - model = gtk_tree_view_get_model (tree_view); +/* KEEP IN SYNC WITH GTK_TREE_VIEW_BIN_EXPOSE */ +/** + * gtk_tree_view_create_row_drag_icon: + * @tree_view: a #GtkTreeView + * @path: a #GtkTreePath in @tree_view + * + * Creates a GdkPixmap representation of the row at @path. This image is used + * for a drag icon. + * + * Return value: a newly allocatdd pixmap of the drag icon. + **/ +GdkPixmap * +gtk_tree_view_create_row_drag_icon (GtkTreeView *tree_view, + GtkTreePath *path) +{ + GtkTreeIter iter; + GtkRBTree *tree; + GtkRBNode *node; + GtkCellRenderer *cell; + gint i; + gint cell_offset; + GList *list; + GdkRectangle background_area; + GtkWidget *widget; + gint depth; + /* start drawing inside the black outline */ + gint x = 1, y = 1; + GdkDrawable *drawable; + gint bin_window_width; - if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received")) - return; + widget = GTK_WIDGET (tree_view); - di = get_info (tree_view); + depth = gtk_tree_path_get_depth (path); - if (di == NULL) - return; + _gtk_tree_view_find_node (tree_view, + path, + &tree, + &node); - suggested_action = get_status_pending (context); + if (tree == NULL) + return NULL; - if (suggested_action) - { - /* We are getting this data due to a request in drag_motion, - * rather than due to a request in drag_drop, so we are just - * supposed to call drag_status, not actually paste in the - * data. - */ - path = get_logical_dest_row (tree_view); + if (!gtk_tree_model_get_iter (tree_view->priv->model, + &iter, + path)) + return NULL; - if (path == NULL) - suggested_action = 0; + cell_offset = x; - if (suggested_action) - { - GtkTreeModel *src_model = NULL; - GtkTreePath *src_path = NULL; + background_area.y = y; + background_area.height = BACKGROUND_HEIGHT (node); - if (!gtk_selection_data_get_tree_row (selection_data, - &src_model, - &src_path)) - suggested_action = 0; + gdk_drawable_get_size (tree_view->priv->bin_window, + &bin_window_width, NULL); - if (suggested_action) - { - if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model), - src_model, - src_path, - path)) - suggested_action = 0; + drawable = gdk_pixmap_new (tree_view->priv->bin_window, + bin_window_width + 2, + background_area.height + 2, + -1); - gtk_tree_path_free (src_path); - } - } + gdk_draw_rectangle (drawable, + widget->style->base_gc[GTK_WIDGET_STATE (widget)], + TRUE, + 0, 0, + bin_window_width + 2, + background_area.height + 2); - gdk_drag_status (context, suggested_action, time); + gdk_draw_rectangle (drawable, + widget->style->black_gc, + FALSE, + 0, 0, + bin_window_width + 1, + background_area.height + 1); - if (path) - gtk_tree_path_free (path); + for (i = 0, list = tree_view->priv->columns; i < tree_view->priv->n_columns; i++, list = list->next) + { + GtkTreeViewColumn *column = list->data; + GdkRectangle cell_area; + gboolean visible; + gint vertical_separator; - /* If you can't drop, remove user drop indicator until the next motion */ - if (suggested_action == 0) - gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (widget), - NULL, - GTK_TREE_VIEW_DROP_BEFORE); + if (!column->visible) + continue; - return; - } + cell = column->cell; + gtk_tree_view_column_set_cell_data (column, + tree_view->priv->model, + &iter); - dest_row = get_dest_row (context); + background_area.x = cell_offset; + background_area.width = column->displayed_width; - if (dest_row == NULL) - return; + cell_area = background_area; - if (selection_data->length >= 0) - { - if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model), - dest_row, - selection_data)) - accepted = TRUE; - } + gtk_widget_style_get (widget, "vertical_separator", &vertical_separator, NULL); + cell_area.y += vertical_separator / 2; + cell_area.height -= vertical_separator; - gtk_drag_finish (context, - accepted, - (context->action == GDK_ACTION_MOVE), - time); + if (i == tree_view->priv->expander_column && + TREE_VIEW_DRAW_EXPANDERS(tree_view)) + { + cell_area.x += depth * tree_view->priv->tab_offset; + cell_area.width -= depth * tree_view->priv->tab_offset; + } - gtk_tree_path_free (dest_row); + g_object_get (G_OBJECT (cell), "visible", &visible, NULL); + if (visible) + gtk_cell_renderer_render (cell, + drawable, + widget, + &background_area, + &cell_area, + NULL, + 0); - /* drop dest_row */ - set_dest_row (context, NULL, NULL); + cell_offset += column->displayed_width; + } + + return drawable; } + diff --git a/gtk/gtktreeview.h b/gtk/gtktreeview.h index 61e234321a..f6ba3a2f18 100644 --- a/gtk/gtktreeview.h +++ b/gtk/gtktreeview.h @@ -46,14 +46,12 @@ typedef enum #define GTK_IS_TREE_VIEW(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_TREE_VIEW)) #define GTK_IS_TREE_VIEW_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_TREE_VIEW)) -typedef struct _GtkTreeView GtkTreeView; -typedef struct _GtkTreeViewClass GtkTreeViewClass; -typedef struct _GtkTreeViewPrivate GtkTreeViewPrivate; - +typedef struct _GtkTreeView GtkTreeView; +typedef struct _GtkTreeViewClass GtkTreeViewClass; +typedef struct _GtkTreeViewPrivate GtkTreeViewPrivate; typedef struct _GtkTreeSelection GtkTreeSelection; typedef struct _GtkTreeSelectionClass GtkTreeSelectionClass; - struct _GtkTreeView { GtkContainer parent; @@ -80,163 +78,158 @@ struct _GtkTreeViewClass }; -typedef gboolean (* GtkTreeViewColumnDropFunc) (GtkTreeView *tree_view, - GtkTreeViewColumn *column, - GtkTreeViewColumn *prev_column, - GtkTreeViewColumn *next_column, - gpointer data); -typedef gboolean (* GtkTreeViewDraggableFunc) (GtkTreeView *tree_view, - GdkDragContext *context, - GtkTreePath *path, - gpointer user_data); -typedef void (* GtkTreeViewMappingFunc) (GtkTreeView *tree_view, - GtkTreePath *path, - gpointer user_data); - - - +typedef gboolean (* GtkTreeViewColumnDropFunc) (GtkTreeView *tree_view, + GtkTreeViewColumn *column, + GtkTreeViewColumn *prev_column, + GtkTreeViewColumn *next_column, + gpointer data); +typedef gboolean (* GtkTreeViewDraggableFunc) (GtkTreeView *tree_view, + GdkDragContext *context, + GtkTreePath *path, + gpointer user_data); +typedef void (* GtkTreeViewMappingFunc) (GtkTreeView *tree_view, + GtkTreePath *path, + gpointer user_data); +typedef gboolean (* GtkTreeViewDroppableFunc) (GtkTreeView *tree_view, + GdkDragContext *context, + GtkTreePath *path, + GtkTreeViewDropPosition *pos, + gpointer user_data); + + +/* Creators */ GtkType gtk_tree_view_get_type (void); GtkWidget *gtk_tree_view_new (void); -GtkWidget *gtk_tree_view_new_with_model (GtkTreeModel *model); -GtkTreeModel *gtk_tree_view_get_model (GtkTreeView *tree_view); -void gtk_tree_view_set_model (GtkTreeView *tree_view, - GtkTreeModel *model); -GtkTreeSelection *gtk_tree_view_get_selection (GtkTreeView *tree_view); -GtkAdjustment *gtk_tree_view_get_hadjustment (GtkTreeView *tree_view); -void gtk_tree_view_set_hadjustment (GtkTreeView *tree_view, - GtkAdjustment *adjustment); -GtkAdjustment *gtk_tree_view_get_vadjustment (GtkTreeView *tree_view); -void gtk_tree_view_set_vadjustment (GtkTreeView *tree_view, - GtkAdjustment *adjustment); -gboolean gtk_tree_view_get_headers_visible (GtkTreeView *tree_view); -void gtk_tree_view_set_headers_visible (GtkTreeView *tree_view, - gboolean headers_visible); -void gtk_tree_view_columns_autosize (GtkTreeView *tree_view); -void gtk_tree_view_set_headers_clickable (GtkTreeView *tree_view, - gboolean setting); -gint gtk_tree_view_append_column (GtkTreeView *tree_view, - GtkTreeViewColumn *column); -gint gtk_tree_view_remove_column (GtkTreeView *tree_view, - GtkTreeViewColumn *column); -gint gtk_tree_view_insert_column (GtkTreeView *tree_view, - GtkTreeViewColumn *column, - gint position); -gint gtk_tree_view_insert_column_with_attributes (GtkTreeView *tree_view, - gint position, - gchar *title, - GtkCellRenderer *cell, +GtkWidget *gtk_tree_view_new_with_model (GtkTreeModel *model); + +/* Accessors */ +GtkTreeModel *gtk_tree_view_get_model (GtkTreeView *tree_view); +void gtk_tree_view_set_model (GtkTreeView *tree_view, + GtkTreeModel *model); +GtkTreeSelection *gtk_tree_view_get_selection (GtkTreeView *tree_view); +GtkAdjustment *gtk_tree_view_get_hadjustment (GtkTreeView *tree_view); +void gtk_tree_view_set_hadjustment (GtkTreeView *tree_view, + GtkAdjustment *adjustment); +GtkAdjustment *gtk_tree_view_get_vadjustment (GtkTreeView *tree_view); +void gtk_tree_view_set_vadjustment (GtkTreeView *tree_view, + GtkAdjustment *adjustment); +gboolean gtk_tree_view_get_headers_visible (GtkTreeView *tree_view); +void gtk_tree_view_set_headers_visible (GtkTreeView *tree_view, + gboolean headers_visible); +void gtk_tree_view_columns_autosize (GtkTreeView *tree_view); +void gtk_tree_view_set_headers_clickable (GtkTreeView *tree_view, + gboolean setting); +void gtk_tree_view_set_rules_hint (GtkTreeView *tree_view, + gboolean setting); +gboolean gtk_tree_view_get_rules_hint (GtkTreeView *tree_view); + +/* Column funtions */ +gint gtk_tree_view_append_column (GtkTreeView *tree_view, + GtkTreeViewColumn *column); +gint gtk_tree_view_remove_column (GtkTreeView *tree_view, + GtkTreeViewColumn *column); +gint gtk_tree_view_insert_column (GtkTreeView *tree_view, + GtkTreeViewColumn *column, + gint position); +gint gtk_tree_view_insert_column_with_attributes (GtkTreeView *tree_view, + gint position, + gchar *title, + GtkCellRenderer *cell, ...); -void gtk_tree_view_move_column (GtkTreeView *tree_view, - GtkTreeViewColumn *column, - GtkTreeViewColumn *base_column, - gint side); -GtkTreeViewColumn *gtk_tree_view_get_column (GtkTreeView *tree_view, - gint n); -void gtk_tree_view_set_expander_column (GtkTreeView *tree_view, - gint col); -gint gtk_tree_view_get_expander_column (GtkTreeView *tree_view); - +GtkTreeViewColumn *gtk_tree_view_get_column (GtkTreeView *tree_view, + gint n); +void gtk_tree_view_move_column (GtkTreeView *tree_view, + GtkTreeViewColumn *column, + GtkTreeViewColumn *base_column, + gint left); +void gtk_tree_view_set_expander_column (GtkTreeView *tree_view, + gint col); +gint gtk_tree_view_get_expander_column (GtkTreeView *tree_view); /* Actions */ -void gtk_tree_view_scroll_to_point (GtkTreeView *tree_view, - gint tree_x, - gint tree_y); -void gtk_tree_view_scroll_to_cell (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column, - gfloat row_align, - gfloat col_align); -gboolean gtk_tree_view_get_path_at_pos (GtkTreeView *tree_view, - GdkWindow *window, - gint x, - gint y, - GtkTreePath **path, - GtkTreeViewColumn **column, - gint *cell_x, - gint *cell_y); -void gtk_tree_view_get_cell_area (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column, - GdkRectangle *rect); -void gtk_tree_view_get_background_area (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column, - GdkRectangle *rect); -void gtk_tree_view_expand_all (GtkTreeView *tree_view); -void gtk_tree_view_collapse_all (GtkTreeView *tree_view); -gboolean gtk_tree_view_expand_row (GtkTreeView *tree_view, - GtkTreePath *path, - gboolean open_all); -gboolean gtk_tree_view_collapse_row (GtkTreeView *tree_view, - GtkTreePath *path); -void gtk_tree_view_row_activated (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column); -void gtk_tree_view_map_expanded_rows (GtkTreeView *tree_view, - GtkTreeViewMappingFunc func, - gpointer data); - -void gtk_tree_view_get_visible_rect (GtkTreeView *tree_view, - GdkRectangle *visible_rect); -void gtk_tree_view_widget_to_tree_coords (GtkTreeView *tree_view, - gint wx, - gint wy, - gint *tx, - gint *ty); -void gtk_tree_view_tree_to_widget_coords (GtkTreeView *tree_view, - gint tx, - gint ty, - gint *wx, - gint *wy); - -void gtk_tree_view_set_rules_hint (GtkTreeView *tree_view, - gboolean setting); -gboolean gtk_tree_view_get_rules_hint (GtkTreeView *tree_view); - +void gtk_tree_view_scroll_to_point (GtkTreeView *tree_view, + gint tree_x, + gint tree_y); +void gtk_tree_view_scroll_to_cell (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gfloat row_align, + gfloat col_align); +void gtk_tree_view_row_activated (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column); +void gtk_tree_view_expand_all (GtkTreeView *tree_view); +void gtk_tree_view_collapse_all (GtkTreeView *tree_view); +gboolean gtk_tree_view_expand_row (GtkTreeView *tree_view, + GtkTreePath *path, + gboolean open_all); +gboolean gtk_tree_view_collapse_row (GtkTreeView *tree_view, + GtkTreePath *path); +void gtk_tree_view_map_expanded_rows (GtkTreeView *tree_view, + GtkTreeViewMappingFunc func, + gpointer data); + +/* Layout information */ +gboolean gtk_tree_view_get_path_at_pos (GtkTreeView *tree_view, + GdkWindow *window, + gint x, + gint y, + GtkTreePath **path, + GtkTreeViewColumn **column, + gint *cell_x, + gint *cell_y); +void gtk_tree_view_get_cell_area (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + GdkRectangle *rect); +void gtk_tree_view_get_background_area (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + GdkRectangle *rect); +void gtk_tree_view_get_visible_rect (GtkTreeView *tree_view, + GdkRectangle *visible_rect); +void gtk_tree_view_widget_to_tree_coords (GtkTreeView *tree_view, + gint wx, + gint wy, + gint *tx, + gint *ty); +void gtk_tree_view_tree_to_widget_coords (GtkTreeView *tree_view, + gint tx, + gint ty, + gint *wx, + gint *wy); /* Drag-and-Drop support */ -/* this func can change "pos" if it likes, in addition to returning - * true/false for whether a drop is possible - */ -typedef gboolean (* GtkTreeViewDroppableFunc) (GtkTreeView *tree_view, - GdkDragContext *context, - GtkTreePath *path, - GtkTreeViewDropPosition *pos, - gpointer user_data); - -void gtk_tree_view_set_rows_drag_source (GtkTreeView *tree_view, - GdkModifierType start_button_mask, - const GtkTargetEntry *targets, - gint n_targets, - GdkDragAction actions, - GtkTreeViewDraggableFunc row_draggable_func, - gpointer user_data); -void gtk_tree_view_set_rows_drag_dest (GtkTreeView *tree_view, - const GtkTargetEntry *targets, - gint n_targets, - GdkDragAction actions, - GtkTreeViewDroppableFunc location_droppable_func, - gpointer user_data); - -void gtk_tree_view_unset_rows_drag_source (GtkTreeView *tree_view); -void gtk_tree_view_unset_rows_drag_dest (GtkTreeView *tree_view); +void gtk_tree_view_set_rows_drag_source (GtkTreeView *tree_view, + GdkModifierType start_button_mask, + const GtkTargetEntry *targets, + gint n_targets, + GdkDragAction actions, + GtkTreeViewDraggableFunc row_draggable_func, + gpointer user_data); +void gtk_tree_view_set_rows_drag_dest (GtkTreeView *tree_view, + const GtkTargetEntry *targets, + gint n_targets, + GdkDragAction actions, + GtkTreeViewDroppableFunc location_droppable_func, + gpointer user_data); +void gtk_tree_view_unset_rows_drag_source (GtkTreeView *tree_view); +void gtk_tree_view_unset_rows_drag_dest (GtkTreeView *tree_view); /* These are useful to implement your own custom stuff. */ -void gtk_tree_view_set_drag_dest_row (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewDropPosition pos); -void gtk_tree_view_get_drag_dest_row (GtkTreeView *tree_view, - GtkTreePath **path, - GtkTreeViewDropPosition *pos); -gboolean gtk_tree_view_get_dest_row_at_pos (GtkTreeView *tree_view, - gint drag_x, - gint drag_y, - GtkTreePath **path, - GtkTreeViewDropPosition *pos); -GdkPixmap* gtk_tree_view_create_row_drag_icon (GtkTreeView *tree_view, - GtkTreePath *path); - - +void gtk_tree_view_set_drag_dest_row (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewDropPosition pos); +void gtk_tree_view_get_drag_dest_row (GtkTreeView *tree_view, + GtkTreePath **path, + GtkTreeViewDropPosition *pos); +gboolean gtk_tree_view_get_dest_row_at_pos (GtkTreeView *tree_view, + gint drag_x, + gint drag_y, + GtkTreePath **path, + GtkTreeViewDropPosition *pos); +GdkPixmap *gtk_tree_view_create_row_drag_icon (GtkTreeView *tree_view, + GtkTreePath *path); #ifdef __cplusplus } -- 2.30.2